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
.IOUtils
;
21 import be
.nikiroo
.utils
.Progress
;
24 * Manage a library of Stories: import, export, list.
26 * Each {@link Story} object will be associated with a (local to the library)
27 * unique ID, the LUID, which will be used to identify the {@link Story}.
31 public class Library
{
33 private Map
<MetaData
, File
> stories
;
35 private OutputType text
;
36 private OutputType image
;
39 * Create a new {@link Library} with the given backend directory.
42 * the directory where to find the {@link Story} objects
44 * the {@link OutputType} to save the text-focused stories into
46 * the {@link OutputType} to save the images-focused stories into
48 public Library(File dir
, OutputType text
, OutputType image
) {
50 this.stories
= new HashMap
<MetaData
, File
>();
59 * List all the known types of stories.
63 public synchronized List
<String
> getTypes() {
64 List
<String
> list
= new ArrayList
<String
>();
65 for (Entry
<MetaData
, File
> entry
: getStories().entrySet()) {
66 String storyType
= entry
.getValue().getParentFile().getName();
67 if (!list
.contains(storyType
)) {
76 * List all the stories of the given source type in the {@link Library}, or
77 * all the stories if NULL is passed as a type.
80 * the type of story to retrieve, or NULL for all
84 public synchronized List
<MetaData
> getList(String type
) {
85 List
<MetaData
> list
= new ArrayList
<MetaData
>();
86 for (Entry
<MetaData
, File
> entry
: getStories().entrySet()) {
87 String storyType
= entry
.getValue().getParentFile().getName();
88 if (type
== null || type
.equalsIgnoreCase(storyType
)) {
89 list
.add(entry
.getKey());
97 * Retrieve a {@link File} corresponding to the given {@link Story}.
100 * the Library UID of the story
102 * @return the corresponding {@link Story}
104 public synchronized MetaData
getInfo(String luid
) {
106 for (Entry
<MetaData
, File
> entry
: getStories().entrySet()) {
107 if (luid
.equals(entry
.getKey().getLuid())) {
108 return entry
.getKey();
117 * Retrieve a {@link File} corresponding to the given {@link Story}.
120 * the Library UID of the story
122 * @return the corresponding {@link Story}
124 public synchronized File
getFile(String luid
) {
126 for (Entry
<MetaData
, File
> entry
: getStories().entrySet()) {
127 if (luid
.equals(entry
.getKey().getLuid())) {
128 return entry
.getValue();
137 * Retrieve a specific {@link Story}.
140 * the Library UID of the story
142 * the optional progress reporter
144 * @return the corresponding {@link Story} or NULL if not found
146 public synchronized Story
getStory(String luid
, Progress pg
) {
148 for (Entry
<MetaData
, File
> entry
: getStories().entrySet()) {
149 if (luid
.equals(entry
.getKey().getLuid())) {
151 SupportType type
= SupportType
.valueOfAllOkUC(entry
152 .getKey().getType());
153 URL url
= entry
.getValue().toURI().toURL();
155 return BasicSupport
.getSupport(type
).process(url
,
158 throw new IOException("Unknown type: "
159 + entry
.getKey().getType());
161 } catch (IOException e
) {
162 // We should not have not-supported files in the
164 Instance
.syserr(new IOException(
165 "Cannot load file from library: "
166 + entry
.getValue().getPath(), e
));
181 * Import the {@link Story} at the given {@link URL} into the
185 * the {@link URL} to import
187 * the optional progress reporter
189 * @return the imported {@link Story}
191 * @throws IOException
192 * in case of I/O error
194 public Story
imprt(URL url
, Progress pg
) throws IOException
{
195 BasicSupport support
= BasicSupport
.getSupport(url
);
196 if (support
== null) {
197 throw new IOException("URL not supported: " + url
.toString());
200 return save(support
.process(url
, pg
), null);
204 * Export the {@link Story} to the given target in the given format.
207 * the {@link Story} ID
209 * the {@link OutputType} to transform it to
211 * the target to save to
213 * the optional progress reporter
215 * @return the saved resource (the main saved {@link File})
217 * @throws IOException
218 * in case of I/O error
220 public File
export(String luid
, OutputType type
, String target
, Progress pg
)
222 Progress pgGetStory
= new Progress();
223 Progress pgOut
= new Progress();
226 pg
.addProgress(pgGetStory
, 1);
227 pg
.addProgress(pgOut
, 1);
230 BasicOutput out
= BasicOutput
.getOutput(type
, true);
232 throw new IOException("Output type not supported: " + type
);
235 Story story
= getStory(luid
, pgGetStory
);
237 throw new IOException("Cannot find story to export: " + luid
);
240 return out
.process(story
, target
, pgOut
);
244 * Save a {@link Story} to the {@link Library}.
247 * the {@link Story} to save
249 * the optional progress reporter
251 * @return the same {@link Story}, whose LUID may have changed
253 * @throws IOException
254 * in case of I/O error
256 public Story
save(Story story
, Progress pg
) throws IOException
{
257 return save(story
, null, pg
);
261 * Save a {@link Story} to the {@link Library} -- the LUID <b>must</b> be
262 * correct, or NULL to get the next free one.
265 * the {@link Story} to save
267 * the <b>correct</b> LUID or NULL to get the next free one
269 * the optional progress reporter
271 * @return the same {@link Story}, whose LUID may have changed
273 * @throws IOException
274 * in case of I/O error
276 public synchronized Story
save(Story story
, String luid
, Progress pg
)
278 // Do not change the original metadata, but change the original story
279 MetaData key
= story
.getMeta().clone();
282 if (luid
== null || luid
.isEmpty()) {
283 getStories(); // refresh lastId if needed
284 key
.setLuid(String
.format("%03d", (++lastId
)));
289 getDir(key
).mkdirs();
290 if (!getDir(key
).exists()) {
291 throw new IOException("Cannot create library dir");
295 if (key
!= null && key
.isImageDocument()) {
301 BasicOutput it
= BasicOutput
.getOutput(out
, true);
302 it
.process(story
, getFile(key
).getPath(), pg
);
311 * Delete the given {@link Story} from this {@link Library}.
314 * the LUID of the target {@link Story}
316 * @return TRUE if it was deleted
318 public synchronized boolean delete(String luid
) {
321 MetaData meta
= getInfo(luid
);
322 File file
= getStories().get(meta
);
326 String readerExt
= getOutputType(meta
)
327 .getDefaultExtension(true);
328 String fileExt
= getOutputType(meta
).getDefaultExtension(false);
330 String path
= file
.getAbsolutePath();
331 if (readerExt
!= null && !readerExt
.equals(fileExt
)) {
333 .substring(0, path
.length() - readerExt
.length())
335 file
= new File(path
);
336 IOUtils
.deltree(file
);
339 File infoFile
= new File(path
+ ".info");
340 if (!infoFile
.exists()) {
341 infoFile
= new File(path
.substring(0, path
.length()
347 String coverExt
= "."
348 + Instance
.getConfig().getString(
349 Config
.IMAGE_FORMAT_COVER
);
350 File coverFile
= new File(path
+ coverExt
);
351 if (!coverFile
.exists()) {
352 coverFile
= new File(path
.substring(0, path
.length()
353 - fileExt
.length()));
368 * The directory (full path) where the {@link Story} related to this
369 * {@link MetaData} should be located on disk.
372 * the {@link Story} {@link MetaData}
374 * @return the target directory
376 private File
getDir(MetaData key
) {
377 String source
= key
.getSource().replaceAll("[^a-zA-Z0-9._+-]", "_");
378 return new File(baseDir
, source
);
382 * The target (full path) where the {@link Story} related to this
383 * {@link MetaData} should be located on disk.
386 * the {@link Story} {@link MetaData}
390 private File
getFile(MetaData key
) {
391 String title
= key
.getTitle().replaceAll("[^a-zA-Z0-9._+-]", "_");
392 return new File(getDir(key
), key
.getLuid() + "_" + title
);
396 * Return all the known stories in this {@link Library} object.
398 * @return the stories
400 private synchronized Map
<MetaData
, File
> getStories() {
401 if (stories
.isEmpty()) {
404 String ext
= ".info";
405 for (File dir
: baseDir
.listFiles()) {
406 if (dir
.isDirectory()) {
407 for (File file
: dir
.listFiles()) {
409 if (file
.getPath().toLowerCase().endsWith(ext
)) {
410 MetaData meta
= InfoReader
.readMeta(file
);
412 int id
= Integer
.parseInt(meta
.getLuid());
417 // Replace .info with whatever is needed:
418 String path
= file
.getPath();
419 path
= path
.substring(0, path
.length()
422 String newExt
= getOutputType(meta
)
423 .getDefaultExtension(true);
425 file
= new File(path
+ newExt
);
428 stories
.put(meta
, file
);
430 } catch (Exception e
) {
432 Instance
.syserr(new IOException(
433 "Cannot understand the LUID of "
434 + file
.getPath() + ": "
435 + meta
.getLuid(), e
));
438 } catch (IOException e
) {
439 // We should not have not-supported files in the
441 Instance
.syserr(new IOException(
442 "Cannot load file from library: "
443 + file
.getPath(), e
));
454 * Return the {@link OutputType} for this {@link Story}.
457 * the {@link Story} {@link MetaData}
461 private OutputType
getOutputType(MetaData meta
) {
462 if (meta
!= null && meta
.isImageDocument()) {