tests: fix NPE, add BasicSupportUtilities tests
[nikiroo-utils.git] / src / be / nikiroo / fanfix / supported / Epub.java
1 package be.nikiroo.fanfix.supported;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.net.URISyntaxException;
7 import java.net.URL;
8 import java.net.URLDecoder;
9 import java.util.ArrayList;
10 import java.util.zip.ZipEntry;
11 import java.util.zip.ZipInputStream;
12
13 import org.jsoup.nodes.Document;
14
15 import be.nikiroo.fanfix.Instance;
16 import be.nikiroo.fanfix.data.MetaData;
17 import be.nikiroo.utils.IOUtils;
18 import be.nikiroo.utils.Image;
19 import be.nikiroo.utils.StringUtils;
20 import be.nikiroo.utils.streams.MarkableFileInputStream;
21
22 /**
23 * Support class for EPUB files created with this program (as we need some
24 * metadata available in those we create).
25 *
26 * @author niki
27 */
28 class Epub extends InfoText {
29 private MetaData meta;
30 private File tmpDir;
31 private String desc;
32
33 private URL fakeSource;
34 private InputStream fakeIn;
35
36 public File getSourceFileOriginal() {
37 return super.getSourceFile();
38 }
39
40 @Override
41 protected File getSourceFile() {
42 try {
43 return new File(fakeSource.toURI());
44 } catch (URISyntaxException e) {
45 Instance.getTraceHandler()
46 .error(new IOException(
47 "Cannot get the source file from the info-text URL",
48 e));
49 }
50
51 return null;
52 }
53
54 @Override
55 protected InputStream getInput() {
56 if (fakeIn != null) {
57 try {
58 fakeIn.reset();
59 } catch (IOException e) {
60 Instance.getTraceHandler()
61 .error(new IOException(
62 "Cannot reset the Epub Text stream", e));
63 }
64
65 return fakeIn;
66 }
67
68 return null;
69 }
70
71 @Override
72 protected boolean supports(URL url) {
73 return url.getPath().toLowerCase().endsWith(".epub");
74 }
75
76 @Override
77 protected MetaData getMeta() throws IOException {
78 return meta;
79 }
80
81 @Override
82 protected Document loadDocument(URL source) throws IOException {
83 super.loadDocument(source); // prepares super.getSourceFile() and
84 // super.getInput()
85
86 InputStream in = super.getInput();
87 ZipInputStream zipIn = null;
88 try {
89 zipIn = new ZipInputStream(in);
90 tmpDir = Instance.getTempFiles().createTempDir(
91 "fanfic-reader-parser");
92 File tmp = new File(tmpDir, "file.txt");
93 File tmpInfo = new File(tmpDir, "file.info");
94
95 fakeSource = tmp.toURI().toURL();
96 Image cover = null;
97
98 String url;
99 try {
100 url = getSource().toURI().toURL().toString();
101 } catch (URISyntaxException e1) {
102 url = getSource().toString();
103 }
104 String title = null;
105 String author = null;
106
107 for (ZipEntry entry = zipIn.getNextEntry(); entry != null; entry = zipIn
108 .getNextEntry()) {
109 if (!entry.isDirectory()
110 && entry.getName().startsWith(getDataPrefix())) {
111 String entryLName = entry.getName().toLowerCase();
112
113 boolean imageEntry = false;
114 for (String ext : bsImages.getImageExt(false)) {
115 if (entryLName.endsWith(ext)) {
116 imageEntry = true;
117 }
118 }
119
120 if (entry.getName().equals(getDataPrefix() + "version")) {
121 // Nothing to do for now ("first"
122 // version is 3.0)
123 } else if (entryLName.endsWith(".info")) {
124 // Info file
125 IOUtils.write(zipIn, tmpInfo);
126 } else if (imageEntry) {
127 // Cover
128 if (getCover()) {
129 try {
130 cover = new Image(zipIn);
131 } catch (Exception e) {
132 Instance.getTraceHandler().error(e);
133 }
134 }
135 } else if (entry.getName().equals(getDataPrefix() + "URL")) {
136 String[] descArray = StringUtils
137 .unhtml(IOUtils.readSmallStream(zipIn)).trim()
138 .split("\n");
139 if (descArray.length > 0) {
140 url = descArray[0].trim();
141 }
142 } else if (entry.getName().equals(
143 getDataPrefix() + "SUMMARY")) {
144 String[] descArray = StringUtils
145 .unhtml(IOUtils.readSmallStream(zipIn)).trim()
146 .split("\n");
147 int skip = 0;
148 if (descArray.length > 1) {
149 title = descArray[0].trim();
150 skip = 1;
151 if (descArray.length > 2
152 && descArray[1].startsWith("©")) {
153 author = descArray[1].substring(1).trim();
154 skip = 2;
155 }
156 }
157 this.desc = "";
158 for (int i = skip; i < descArray.length; i++) {
159 this.desc += descArray[i].trim() + "\n";
160 }
161
162 this.desc = this.desc.trim();
163 } else {
164 // Hopefully the data file
165 IOUtils.write(zipIn, tmp);
166 }
167 }
168 }
169
170 if (requireInfo() && (!tmp.exists() || !tmpInfo.exists())) {
171 throw new IOException(
172 "file not supported (maybe not created with this program or corrupt)");
173 }
174
175 if (tmp.exists()) {
176 this.fakeIn = new MarkableFileInputStream(tmp);
177 }
178
179 if (tmpInfo.exists()) {
180 meta = InfoReader.readMeta(tmpInfo, true);
181 tmpInfo.delete();
182 } else {
183 if (title == null || title.isEmpty()) {
184 title = getSourceFileOriginal().getName();
185 if (title.toLowerCase().endsWith(".cbz")) {
186 title = title.substring(0, title.length() - 4);
187 }
188 title = URLDecoder.decode(title, "UTF-8").trim();
189 }
190
191 meta = new MetaData();
192 meta.setLang("en");
193 meta.setTags(new ArrayList<String>());
194 meta.setSource(getType().getSourceName());
195 meta.setUuid(url);
196 meta.setUrl(url);
197 meta.setTitle(title);
198 meta.setAuthor(author);
199 meta.setImageDocument(isImagesDocumentByDefault());
200 }
201
202 if (meta.getCover() == null) {
203 if (cover != null) {
204 meta.setCover(cover);
205 } else {
206 meta.setCover(InfoReader
207 .getCoverByName(getSourceFileOriginal().toURI()
208 .toURL()));
209 }
210 }
211 } finally {
212 if (zipIn != null) {
213 zipIn.close();
214 }
215 if (in != null) {
216 in.close();
217 }
218 }
219
220 return null;
221 }
222
223 @Override
224 protected void close() {
225 if (tmpDir != null) {
226 IOUtils.deltree(tmpDir);
227 }
228
229 tmpDir = null;
230
231 super.close();
232 }
233
234 protected String getDataPrefix() {
235 return "DATA/";
236 }
237
238 protected boolean requireInfo() {
239 return true;
240 }
241
242 protected boolean getCover() {
243 return true;
244 }
245
246 protected boolean isImagesDocumentByDefault() {
247 return false;
248 }
249 }