Initial commit (working)
[fanfix.git] / src / be / nikiroo / fanfix / Library.java
1 package be.nikiroo.fanfix;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.net.URL;
6 import java.util.ArrayList;
7 import java.util.HashMap;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Map.Entry;
11
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
20 /**
21 * Manage a library of Stories: import, export, list.
22 * <p>
23 * Each {@link Story} object will be associated with a (local to the library)
24 * unique ID, the LUID, which will be used to identify the {@link Story}.
25 *
26 * @author niki
27 */
28 public class Library {
29 private File baseDir;
30 private Map<MetaData, File> stories;
31 private BasicSupport itSupport = BasicSupport
32 .getSupport(SupportType.INFO_TEXT);
33 private int lastId;
34
35 /**
36 * Create a new {@link Library} with the given backend directory.
37 *
38 * @param dir
39 * the directoy where to find the {@link Story} objects
40 */
41 public Library(File dir) {
42 this.baseDir = dir;
43 this.stories = new HashMap<MetaData, File>();
44 this.lastId = 0;
45
46 dir.mkdirs();
47 }
48
49 /**
50 * List all the stories of the given source type in the {@link Library}, or
51 * all the stories if NULL is passed as a type.
52 *
53 * @param type
54 * the type of story to retrieve, or NULL for all
55 *
56 * @return the stories
57 */
58 public List<MetaData> getList(SupportType type) {
59 String typeString = type == null ? null : type.getSourceName();
60
61 List<MetaData> list = new ArrayList<MetaData>();
62 for (Entry<MetaData, File> entry : getStories().entrySet()) {
63 String storyType = entry.getValue().getParentFile().getName();
64 if (typeString == null || typeString.equalsIgnoreCase(storyType)) {
65 list.add(entry.getKey());
66 }
67 }
68
69 return list;
70 }
71
72 /**
73 * Retrieve a specific {@link Story}.
74 *
75 * @param luid
76 * the Library UID of the story
77 *
78 * @return the corresponding {@link Story}
79 */
80 public Story getStory(String luid) {
81 if (luid != null) {
82 for (Entry<MetaData, File> entry : getStories().entrySet()) {
83 if (luid.equals(entry.getKey().getLuid())) {
84 try {
85 return itSupport.process(entry.getValue().toURI()
86 .toURL());
87 } catch (IOException e) {
88 // We should not have not-supported files in the
89 // library
90 Instance.syserr(new IOException(
91 "Cannot load file from library: "
92 + entry.getValue().getPath(), e));
93 }
94 }
95 }
96 }
97
98 return null;
99 }
100
101 /**
102 * Import the {@link Story} at the given {@link URL} into the
103 * {@link Library}.
104 *
105 * @param url
106 * the {@link URL} to import
107 *
108 * @return the imported {@link Story}
109 *
110 * @throws IOException
111 * in case of I/O error
112 */
113 public Story imprt(URL url) throws IOException {
114 BasicSupport support = BasicSupport.getSupport(url);
115 if (support == null) {
116 throw new IOException("URL not supported: " + url.toString());
117 }
118
119 getStories(); // refresh lastId
120 Story story = support.process(url);
121 story.getMeta().setLuid(String.format("%03d", (++lastId)));
122 save(story);
123
124 return story;
125 }
126
127 /**
128 * Export the {@link Story} to the given target in the given format.
129 *
130 * @param luid
131 * the {@link Story} ID
132 * @param type
133 * the {@link OutputType} to transform it to
134 * @param target
135 * the target to save to
136 *
137 * @return the saved resource (the main saved {@link File})
138 *
139 * @throws IOException
140 * in case of I/O error
141 */
142 public File export(String luid, OutputType type, String target)
143 throws IOException {
144 BasicOutput out = BasicOutput.getOutput(type, true);
145 if (out == null) {
146 throw new IOException("Output type not supported: " + type);
147 }
148
149 return out.process(getStory(luid), target);
150 }
151
152 /**
153 * Save a story as-is to the {@link Library} -- the LUID <b>must</b> be
154 * correct.
155 *
156 * @param story
157 * the {@link Story} to save
158 *
159 * @throws IOException
160 * in case of I/O error
161 */
162 private void save(Story story) throws IOException {
163 MetaData key = story.getMeta();
164
165 getDir(key).mkdirs();
166 if (!getDir(key).exists()) {
167 throw new IOException("Cannot create library dir");
168 }
169
170 OutputType out;
171 SupportType in;
172 if (key != null && key.isImageDocument()) {
173 in = SupportType.CBZ;
174 out = OutputType.CBZ;
175 } else {
176 in = SupportType.INFO_TEXT;
177 out = OutputType.INFO_TEXT;
178 }
179 BasicOutput it = BasicOutput.getOutput(out, true);
180 File file = it.process(story, getFile(key).getPath());
181 getStories().put(
182 BasicSupport.getSupport(in).processMeta(file.toURI().toURL())
183 .getMeta(), file);
184 }
185
186 /**
187 * The directory (full path) where the {@link Story} related to this
188 * {@link MetaData} should be located on disk.
189 *
190 * @param key
191 * the {@link Story} {@link MetaData}
192 *
193 * @return the target directory
194 */
195 private File getDir(MetaData key) {
196 String source = key.getSource().replaceAll("[^a-zA-Z0-9._+-]", "_");
197 return new File(baseDir, source);
198 }
199
200 /**
201 * The target (full path) where the {@link Story} related to this
202 * {@link MetaData} should be located on disk.
203 *
204 * @param key
205 * the {@link Story} {@link MetaData}
206 *
207 * @return the target
208 */
209 private File getFile(MetaData key) {
210 String title = key.getTitle().replaceAll("[^a-zA-Z0-9._+-]", "_");
211 return new File(getDir(key), key.getLuid() + "_" + title);
212 }
213
214 /**
215 * Return all the known stories in this {@link Library} object.
216 *
217 * @return the stories
218 */
219 private Map<MetaData, File> getStories() {
220 if (stories.isEmpty()) {
221 lastId = 0;
222 String format = Instance.getConfig()
223 .getString(Config.IMAGE_FORMAT_COVER).toLowerCase();
224 for (File dir : baseDir.listFiles()) {
225 if (dir.isDirectory()) {
226 for (File file : dir.listFiles()) {
227 try {
228 String path = file.getPath().toLowerCase();
229 if (!path.endsWith(".info")
230 && !path.endsWith(format)) {
231 MetaData meta = itSupport.processMeta(
232 file.toURI().toURL()).getMeta();
233 stories.put(meta, file);
234
235 try {
236 int id = Integer.parseInt(meta.getLuid());
237 if (id > lastId) {
238 lastId = id;
239 }
240 } catch (Exception e) {
241 // not normal!!
242 Instance.syserr(new IOException(
243 "Cannot read the LUID of: "
244 + file.getPath(), e));
245 }
246 }
247 } catch (IOException e) {
248 // We should not have not-supported files in the
249 // library
250 Instance.syserr(new IOException(
251 "Cannot load file from library: "
252 + file.getPath(), e));
253 }
254 }
255 }
256 }
257 }
258
259 return stories;
260 }
261 }