Merge branch 'master' into subtree
[fanfix.git] / supported / InfoReader.java
1 package be.nikiroo.fanfix.supported;
2
3 import java.io.File;
4 import java.io.FileNotFoundException;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.net.URL;
8 import java.util.ArrayList;
9 import java.util.List;
10 import java.util.Scanner;
11
12 import be.nikiroo.fanfix.Instance;
13 import be.nikiroo.fanfix.bundles.Config;
14 import be.nikiroo.fanfix.data.MetaData;
15 import be.nikiroo.utils.Image;
16 import be.nikiroo.utils.streams.MarkableFileInputStream;
17
18 // not complete: no "description" tag
19 public class InfoReader {
20 static protected BasicSupportHelper bsHelper = new BasicSupportHelper();
21 // static protected BasicSupportImages bsImages = new BasicSupportImages();
22 // static protected BasicSupportPara bsPara = new BasicSupportPara(new BasicSupportHelper(), new BasicSupportImages());
23
24 public static MetaData readMeta(File infoFile, boolean withCover)
25 throws IOException {
26 if (infoFile == null) {
27 throw new IOException("File is null");
28 }
29
30 if (infoFile.exists()) {
31 InputStream in = new MarkableFileInputStream(infoFile);
32 try {
33 return createMeta(infoFile.toURI().toURL(), in, withCover);
34 } finally {
35 in.close();
36 }
37 }
38
39 throw new FileNotFoundException(
40 "File given as argument does not exists: "
41 + infoFile.getAbsolutePath());
42 }
43
44 private static MetaData createMeta(URL sourceInfoFile, InputStream in,
45 boolean withCover) throws IOException {
46 MetaData meta = new MetaData();
47
48 meta.setTitle(getInfoTag(in, "TITLE"));
49 meta.setAuthor(getInfoTag(in, "AUTHOR"));
50 meta.setDate(getInfoTag(in, "DATE"));
51 meta.setTags(getInfoTagList(in, "TAGS", ","));
52 meta.setSource(getInfoTag(in, "SOURCE"));
53 meta.setUrl(getInfoTag(in, "URL"));
54 meta.setPublisher(getInfoTag(in, "PUBLISHER"));
55 meta.setUuid(getInfoTag(in, "UUID"));
56 meta.setLuid(getInfoTag(in, "LUID"));
57 meta.setLang(getInfoTag(in, "LANG"));
58 meta.setSubject(getInfoTag(in, "SUBJECT"));
59 meta.setType(getInfoTag(in, "TYPE"));
60 meta.setImageDocument(getInfoTagBoolean(in, "IMAGES_DOCUMENT", false));
61 if (withCover) {
62 String infoTag = getInfoTag(in, "COVER");
63 if (infoTag != null && !infoTag.trim().isEmpty()) {
64 meta.setCover(bsHelper.getImage(null, sourceInfoFile,
65 infoTag));
66 }
67 if (meta.getCover() == null) {
68 // Second chance: try to check for a cover next to the info file
69 meta.setCover(getCoverByName(sourceInfoFile));
70 }
71 }
72 try {
73 meta.setWords(Long.parseLong(getInfoTag(in, "WORDCOUNT")));
74 } catch (NumberFormatException e) {
75 meta.setWords(0);
76 }
77 meta.setCreationDate(getInfoTag(in, "CREATION_DATE"));
78 meta.setFakeCover(Boolean.parseBoolean(getInfoTag(in, "FAKE_COVER")));
79
80 if (withCover && meta.getCover() == null) {
81 meta.setCover(bsHelper.getDefaultCover(meta.getSubject()));
82 }
83
84 return meta;
85 }
86
87 /**
88 * Return the cover image if it is next to the source file.
89 *
90 * @param sourceInfoFile
91 * the source file
92 *
93 * @return the cover if present, NULL if not
94 */
95 public static Image getCoverByName(URL sourceInfoFile) {
96 Image cover = null;
97
98 File basefile = new File(sourceInfoFile.getFile());
99
100 String ext = "."
101 + Instance.getConfig().getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER)
102 .toLowerCase();
103
104 // Without removing ext
105 cover = bsHelper.getImage(null, sourceInfoFile,
106 basefile.getAbsolutePath() + ext);
107
108 // Try without ext
109 String name = basefile.getName();
110 int pos = name.lastIndexOf(".");
111 if (cover == null && pos > 0) {
112 name = name.substring(0, pos);
113 basefile = new File(basefile.getParent(), name);
114
115 cover = bsHelper.getImage(null, sourceInfoFile,
116 basefile.getAbsolutePath() + ext);
117 }
118
119 return cover;
120 }
121
122 private static boolean getInfoTagBoolean(InputStream in, String key,
123 boolean def) throws IOException {
124 Boolean value = getInfoTagBoolean(in, key);
125 return value == null ? def : value;
126 }
127
128 private static Boolean getInfoTagBoolean(InputStream in, String key)
129 throws IOException {
130 String value = getInfoTag(in, key);
131 if (value != null && !value.trim().isEmpty()) {
132 value = value.toLowerCase().trim();
133 return value.equals("1") || value.equals("on")
134 || value.equals("true") || value.equals("yes");
135 }
136
137 return null;
138 }
139
140 private static List<String> getInfoTagList(InputStream in, String key,
141 String separator) throws IOException {
142 List<String> list = new ArrayList<String>();
143 String tt = getInfoTag(in, key);
144 if (tt != null) {
145 for (String tag : tt.split(separator)) {
146 list.add(tag.trim());
147 }
148 }
149
150 return list;
151 }
152
153 /**
154 * Return the value of the given tag in the <tt>.info</tt> file if present.
155 *
156 * @param key
157 * the tag key
158 *
159 * @return the value or NULL
160 *
161 * @throws IOException
162 * in case of I/O error
163 */
164 private static String getInfoTag(InputStream in, String key)
165 throws IOException {
166 key = "^" + key + "=";
167
168 if (in != null) {
169 in.reset();
170 String value = getLine(in, key, 0);
171 if (value != null && !value.isEmpty()) {
172 value = value.trim().substring(key.length() - 1).trim();
173 if (value.startsWith("'") && value.endsWith("'")
174 || value.startsWith("\"") && value.endsWith("\"")) {
175 value = value.substring(1, value.length() - 1).trim();
176 }
177
178 return value;
179 }
180 }
181
182 return null;
183 }
184
185 /**
186 * Return the first line from the given input which correspond to the given
187 * selectors.
188 *
189 * @param in
190 * the input
191 * @param needle
192 * a string that must be found inside the target line (also
193 * supports "^" at start to say "only if it starts with" the
194 * needle)
195 * @param relativeLine
196 * the line to return based upon the target line position (-1 =
197 * the line before, 0 = the target line...)
198 *
199 * @return the line
200 */
201 static private String getLine(InputStream in, String needle,
202 int relativeLine) {
203 return getLine(in, needle, relativeLine, true);
204 }
205
206 /**
207 * Return a line from the given input which correspond to the given
208 * selectors.
209 *
210 * @param in
211 * the input
212 * @param needle
213 * a string that must be found inside the target line (also
214 * supports "^" at start to say "only if it starts with" the
215 * needle)
216 * @param relativeLine
217 * the line to return based upon the target line position (-1 =
218 * the line before, 0 = the target line...)
219 * @param first
220 * takes the first result (as opposed to the last one, which will
221 * also always spend the input)
222 *
223 * @return the line
224 */
225 static private String getLine(InputStream in, String needle,
226 int relativeLine, boolean first) {
227 String rep = null;
228
229 List<String> lines = new ArrayList<String>();
230 @SuppressWarnings("resource")
231 Scanner scan = new Scanner(in, "UTF-8");
232 int index = -1;
233 scan.useDelimiter("\\n");
234 while (scan.hasNext()) {
235 lines.add(scan.next());
236
237 if (index == -1) {
238 if (needle.startsWith("^")) {
239 if (lines.get(lines.size() - 1).startsWith(
240 needle.substring(1))) {
241 index = lines.size() - 1;
242 }
243
244 } else {
245 if (lines.get(lines.size() - 1).contains(needle)) {
246 index = lines.size() - 1;
247 }
248 }
249 }
250
251 if (index >= 0 && index + relativeLine < lines.size()) {
252 rep = lines.get(index + relativeLine);
253 if (first) {
254 break;
255 }
256 }
257 }
258
259 return rep;
260 }
261 }