1 package be
.nikiroo
.fanfix
.supported
;
4 import java
.io
.FileNotFoundException
;
5 import java
.io
.IOException
;
6 import java
.io
.InputStream
;
8 import java
.util
.ArrayList
;
10 import java
.util
.Scanner
;
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
;
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
23 // BasicSupportHelper(), new BasicSupportImages());
25 public static MetaData
readMeta(File infoFile
, boolean withCover
)
27 if (infoFile
== null) {
28 throw new IOException("File is null");
31 if (infoFile
.exists()) {
32 InputStream in
= new MarkableFileInputStream(infoFile
);
34 MetaData meta
= createMeta(infoFile
.toURI().toURL(), in
,
37 // Some old .info files were using UUID for URL...
38 if (!hasIt(meta
.getUrl()) && meta
.getUuid() != null
39 && (meta
.getUuid().startsWith("http://")
40 || meta
.getUuid().startsWith("https://"))) {
41 meta
.setUrl(meta
.getUuid());
44 // Some old .info files don't have those now required fields...
45 // So we check if we can find the info in another way (many
46 // formats have a copy of the original text file)
47 if (!hasIt(meta
.getTitle(), meta
.getAuthor(), meta
.getDate(),
50 // TODO: not nice, would be better to do it properly...
51 String base
= infoFile
.getPath();
52 if (base
.endsWith(".info")) {
53 base
= base
.substring(0,
54 base
.length() - ".info".length());
56 File textFile
= new File(base
);
57 if (!textFile
.exists()) {
58 textFile
= new File(base
+ ".txt");
60 if (!textFile
.exists()) {
61 textFile
= new File(base
+ ".text");
64 completeMeta(textFile
, meta
);
74 throw new FileNotFoundException(
75 "File given as argument does not exists: "
76 + infoFile
.getAbsolutePath());
80 * Complete the given {@link MetaData} with the original text file if needed
84 * the original text file
86 * the {@link MetaData} to complete if needed and possible
89 * in case of I/O errors
91 static public void completeMeta(File textFile
,
92 MetaData meta
) throws IOException
{
93 if (textFile
!= null && textFile
.exists()) {
94 final URL source
= textFile
.toURI().toURL();
95 final MetaData
[] superMetaA
= new MetaData
[1];
96 @SuppressWarnings("unused")
97 Text unused
= new Text() {
98 private boolean loaded
= loadDocument();
101 public SupportType
getType() {
102 return SupportType
.TEXT
;
105 protected boolean loadDocument() throws IOException
{
106 loadDocument(source
);
107 superMetaA
[0] = getMeta();
112 protected Image
getCover(File sourceFile
) {
117 MetaData superMeta
= superMetaA
[0];
118 if (!hasIt(meta
.getTitle())) {
119 meta
.setTitle(superMeta
.getTitle());
121 if (!hasIt(meta
.getAuthor())) {
122 meta
.setAuthor(superMeta
.getAuthor());
124 if (!hasIt(meta
.getDate())) {
125 meta
.setDate(superMeta
.getDate());
127 if (!hasIt(meta
.getUrl())) {
128 meta
.setUrl(superMeta
.getUrl());
134 * Check if we have non-empty values for all the given {@link String}s.
137 * the values to check
139 * @return TRUE if none of them was NULL or empty
141 static private boolean hasIt(String
... values
) {
142 for (String value
: values
) {
143 if (value
== null || value
.trim().isEmpty()) {
151 private static MetaData
createMeta(URL sourceInfoFile
, InputStream in
,
152 boolean withCover
) throws IOException
{
153 MetaData meta
= new MetaData();
155 meta
.setTitle(getInfoTag(in
, "TITLE"));
156 meta
.setAuthor(getInfoTag(in
, "AUTHOR"));
157 meta
.setDate(bsHelper
.formatDate(getInfoTag(in
, "DATE")));
158 meta
.setTags(getInfoTagList(in
, "TAGS", ","));
159 meta
.setSource(getInfoTag(in
, "SOURCE"));
160 meta
.setUrl(getInfoTag(in
, "URL"));
161 meta
.setPublisher(getInfoTag(in
, "PUBLISHER"));
162 meta
.setUuid(getInfoTag(in
, "UUID"));
163 meta
.setLuid(getInfoTag(in
, "LUID"));
164 meta
.setLang(getInfoTag(in
, "LANG"));
165 meta
.setSubject(getInfoTag(in
, "SUBJECT"));
166 meta
.setType(getInfoTag(in
, "TYPE"));
167 meta
.setImageDocument(getInfoTagBoolean(in
, "IMAGES_DOCUMENT", false));
169 String infoTag
= getInfoTag(in
, "COVER");
170 if (infoTag
!= null && !infoTag
.trim().isEmpty()) {
171 meta
.setCover(bsHelper
.getImage(null, sourceInfoFile
, infoTag
));
173 if (meta
.getCover() == null) {
174 // Second chance: try to check for a cover next to the info file
175 meta
.setCover(getCoverByName(sourceInfoFile
));
179 meta
.setWords(Long
.parseLong(getInfoTag(in
, "WORDCOUNT")));
180 } catch (NumberFormatException e
) {
183 meta
.setCreationDate(
184 bsHelper
.formatDate(getInfoTag(in
, "CREATION_DATE")));
185 meta
.setFakeCover(Boolean
.parseBoolean(getInfoTag(in
, "FAKE_COVER")));
187 if (withCover
&& meta
.getCover() == null) {
188 meta
.setCover(bsHelper
.getDefaultCover(meta
.getSubject()));
195 * Return the cover image if it is next to the source file.
197 * @param sourceInfoFile
200 * @return the cover if present, NULL if not
202 public static Image
getCoverByName(URL sourceInfoFile
) {
205 File basefile
= new File(sourceInfoFile
.getFile());
207 String ext
= "." + Instance
.getInstance().getConfig()
208 .getString(Config
.FILE_FORMAT_IMAGE_FORMAT_COVER
).toLowerCase();
210 // Without removing ext
211 cover
= bsHelper
.getImage(null, sourceInfoFile
,
212 basefile
.getAbsolutePath() + ext
);
215 String name
= basefile
.getName();
216 int pos
= name
.lastIndexOf(".");
217 if (cover
== null && pos
> 0) {
218 name
= name
.substring(0, pos
);
219 basefile
= new File(basefile
.getParent(), name
);
221 cover
= bsHelper
.getImage(null, sourceInfoFile
,
222 basefile
.getAbsolutePath() + ext
);
228 private static boolean getInfoTagBoolean(InputStream in
, String key
,
229 boolean def
) throws IOException
{
230 Boolean value
= getInfoTagBoolean(in
, key
);
231 return value
== null ? def
: value
;
234 private static Boolean
getInfoTagBoolean(InputStream in
, String key
)
236 String value
= getInfoTag(in
, key
);
237 if (value
!= null && !value
.trim().isEmpty()) {
238 value
= value
.toLowerCase().trim();
239 return value
.equals("1") || value
.equals("on")
240 || value
.equals("true") || value
.equals("yes");
246 private static List
<String
> getInfoTagList(InputStream in
, String key
,
247 String separator
) throws IOException
{
248 List
<String
> list
= new ArrayList
<String
>();
249 String tt
= getInfoTag(in
, key
);
251 for (String tag
: tt
.split(separator
)) {
252 list
.add(tag
.trim());
260 * Return the value of the given tag in the <tt>.info</tt> file if present.
265 * @return the value or NULL
267 * @throws IOException
268 * in case of I/O error
270 private static String
getInfoTag(InputStream in
, String key
)
272 key
= "^" + key
+ "=";
276 String value
= getLine(in
, key
, 0);
277 if (value
!= null && !value
.isEmpty()) {
278 value
= value
.trim().substring(key
.length() - 1).trim();
279 if (value
.length() > 1 && //
280 (value
.startsWith("'") && value
.endsWith("'")
281 || value
.startsWith("\"")
282 && value
.endsWith("\""))) {
283 value
= value
.substring(1, value
.length() - 1).trim();
286 // Some old files ended up with TITLE="'xxxxx'"
287 if ("^TITLE=".equals(key
)) {
288 if (value
.startsWith("'") && value
.endsWith("'")
289 && value
.length() > 1) {
290 value
= value
.substring(1, value
.length() - 1).trim();
302 * Return the first line from the given input which correspond to the given
308 * a string that must be found inside the target line (also
309 * supports "^" at start to say "only if it starts with" the
311 * @param relativeLine
312 * the line to return based upon the target line position (-1 =
313 * the line before, 0 = the target line...)
317 static private String
getLine(InputStream in
, String needle
,
319 return getLine(in
, needle
, relativeLine
, true);
323 * Return a line from the given input which correspond to the given
329 * a string that must be found inside the target line (also
330 * supports "^" at start to say "only if it starts with" the
332 * @param relativeLine
333 * the line to return based upon the target line position (-1 =
334 * the line before, 0 = the target line...)
336 * takes the first result (as opposed to the last one, which will
337 * also always spend the input)
341 static private String
getLine(InputStream in
, String needle
,
342 int relativeLine
, boolean first
) {
345 List
<String
> lines
= new ArrayList
<String
>();
346 @SuppressWarnings("resource")
347 Scanner scan
= new Scanner(in
, "UTF-8");
349 scan
.useDelimiter("\\n");
350 while (scan
.hasNext()) {
351 lines
.add(scan
.next());
354 if (needle
.startsWith("^")) {
355 if (lines
.get(lines
.size() - 1)
356 .startsWith(needle
.substring(1))) {
357 index
= lines
.size() - 1;
361 if (lines
.get(lines
.size() - 1).contains(needle
)) {
362 index
= lines
.size() - 1;
367 if (index
>= 0 && index
+ relativeLine
< lines
.size()) {
368 rep
= lines
.get(index
+ relativeLine
);