Version 4.2.1: fix small bug in Downloader
[fanfix.git] / src / be / nikiroo / utils / IOUtils.java
CommitLineData
ec1f3444
NR
1package be.nikiroo.utils;
2
3import java.io.BufferedReader;
80500544 4import java.io.ByteArrayOutputStream;
ec1f3444
NR
5import java.io.File;
6import java.io.FileInputStream;
7import java.io.FileOutputStream;
ec1f3444
NR
8import java.io.FileWriter;
9import java.io.IOException;
10import java.io.InputStream;
3f8349b7 11import java.io.InputStreamReader;
ec1f3444 12import java.io.OutputStream;
59864f77
NR
13import java.util.ArrayList;
14import java.util.List;
ec1f3444
NR
15import java.util.zip.ZipEntry;
16import java.util.zip.ZipOutputStream;
17
18/**
72c32e88 19 * This class offer some utilities based around Streams.
ec1f3444
NR
20 *
21 * @author niki
22 */
23public class IOUtils {
24 /**
25 * Write the data to the given {@link File}.
26 *
27 * @param in
28 * the data source
29 * @param target
30 * the target {@link File}
31 *
32 * @throws IOException
33 * in case of I/O error
34 */
35 public static void write(InputStream in, File target) throws IOException {
36 OutputStream out = new FileOutputStream(target);
37 try {
38 write(in, out);
39 } finally {
40 out.close();
41 }
42 }
43
44 /**
45 * Write the data to the given {@link OutputStream}.
46 *
47 * @param in
48 * the data source
db31c358 49 * @param out
ec1f3444
NR
50 * the target {@link OutputStream}
51 *
52 * @throws IOException
53 * in case of I/O error
54 */
55 public static void write(InputStream in, OutputStream out)
56 throws IOException {
57 byte buffer[] = new byte[4069];
58 for (int len = 0; (len = in.read(buffer)) > 0;) {
59 out.write(buffer, 0, len);
60 }
61 }
62
63 /**
64 * Recursively Add a {@link File} (which can thus be a directory, too) to a
65 * {@link ZipOutputStream}.
66 *
67 * @param zip
68 * the stream
69 * @param base
70 * the path to prepend to the ZIP info before the actual
71 * {@link File} path
72 * @param target
73 * the source {@link File} (which can be a directory)
74 * @param targetIsRoot
75 * FALSE if we need to add a {@link ZipEntry} for base/target,
76 * TRUE to add it at the root of the ZIP
77 *
78 * @throws IOException
79 * in case of I/O error
80 */
81 public static void zip(ZipOutputStream zip, String base, File target,
82 boolean targetIsRoot) throws IOException {
83 if (target.isDirectory()) {
84 if (!targetIsRoot) {
85 if (base == null || base.isEmpty()) {
86 base = target.getName();
87 } else {
88 base += "/" + target.getName();
89 }
90 zip.putNextEntry(new ZipEntry(base + "/"));
91 }
0988831f
NR
92
93 File[] files = target.listFiles();
94 if (files != null) {
95 for (File file : files) {
96 zip(zip, base, file, false);
97 }
ec1f3444
NR
98 }
99 } else {
100 if (base == null || base.isEmpty()) {
101 base = target.getName();
102 } else {
103 base += "/" + target.getName();
104 }
105 zip.putNextEntry(new ZipEntry(base));
106 FileInputStream in = new FileInputStream(target);
107 try {
108 IOUtils.write(in, zip);
109 } finally {
110 in.close();
111 }
112 }
113 }
114
115 /**
116 * Zip the given source into dest.
117 *
118 * @param src
119 * the source {@link File} (which can be a directory)
120 * @param dest
121 * the destination <tt>.zip</tt> file
db31c358 122 * @param srcIsRoot
ec1f3444
NR
123 * FALSE if we need to add a {@link ZipEntry} for src, TRUE to
124 * add it at the root of the ZIP
125 *
126 * @throws IOException
127 * in case of I/O error
128 */
129 public static void zip(File src, File dest, boolean srcIsRoot)
130 throws IOException {
131 OutputStream out = new FileOutputStream(dest);
132 try {
133 ZipOutputStream zip = new ZipOutputStream(out);
134 try {
135 IOUtils.zip(zip, "", src, srcIsRoot);
136 } finally {
137 zip.close();
138 }
139 } finally {
140 out.close();
141 }
142 }
143
144 /**
145 * Write the {@link String} content to {@link File}.
146 *
147 * @param dir
148 * the directory where to write the {@link File}
149 * @param filename
150 * the {@link File} name
151 * @param content
152 * the content
153 *
154 * @throws IOException
155 * in case of I/O error
156 */
157 public static void writeSmallFile(File dir, String filename, String content)
158 throws IOException {
159 if (!dir.exists()) {
160 dir.mkdirs();
161 }
162
163 FileWriter writerVersion = new FileWriter(new File(dir, filename));
164 try {
165 writerVersion.write(content);
166 } finally {
167 writerVersion.close();
168 }
169 }
170
171 /**
172 * Read the whole {@link File} content into a {@link String}.
173 *
174 * @param file
175 * the {@link File}
176 *
177 * @return the content
178 *
179 * @throws IOException
180 * in case of I/O error
181 */
182 public static String readSmallFile(File file) throws IOException {
3f8349b7
NR
183 InputStream stream = new FileInputStream(file);
184 try {
185 return readSmallStream(stream);
186 } finally {
187 stream.close();
188 }
189 }
190
191 /**
192 * Read the whole {@link InputStream} content into a {@link String}.
193 *
194 * @param stream
195 * the {@link InputStream}
196 *
197 * @return the content
198 *
199 * @throws IOException
200 * in case of I/O error
201 */
202 public static String readSmallStream(InputStream stream) throws IOException {
530d4062
NR
203 // do NOT close the reader, or the related stream will be closed, too
204 // reader.close();
3f8349b7
NR
205 BufferedReader reader = new BufferedReader(
206 new InputStreamReader(stream));
530d4062
NR
207
208 StringBuilder builder = new StringBuilder();
209 for (String line = reader.readLine(); line != null; line = reader
210 .readLine()) {
211 builder.append(line);
212 builder.append("\n");
ec1f3444 213 }
530d4062
NR
214
215 return builder.toString();
ec1f3444
NR
216 }
217
59864f77
NR
218 /**
219 * Recursively delete the given {@link File}, which may of course also be a
220 * directory.
221 * <p>
222 * Will either silently continue or throw an exception in case of error,
223 * depending upon the parameters.
224 *
225 * @param target
226 * the target to delete
227 * @param exception
228 * TRUE to throw an {@link IOException} in case of error, FALSE
229 * to silently continue
230 *
231 * @return TRUE if all files were deleted, FALSE if an error occurred
232 *
233 * @throws IOException
234 * if an error occurred and the parameters allow an exception to
235 * be thrown
236 */
237 public static boolean deltree(File target, boolean exception)
238 throws IOException {
239 List<File> list = deltree(target, null);
240 if (exception && !list.isEmpty()) {
0988831f 241 StringBuilder slist = new StringBuilder();
59864f77 242 for (File file : list) {
0988831f 243 slist.append("\n").append(file.getPath());
59864f77
NR
244 }
245
246 throw new IOException("Cannot delete all the files from: <" //
0988831f 247 + target + ">:" + slist.toString());
59864f77
NR
248 }
249
250 return list.isEmpty();
251 }
252
ec1f3444
NR
253 /**
254 * Recursively delete the given {@link File}, which may of course also be a
255 * directory.
256 * <p>
257 * Will silently continue in case of error.
258 *
259 * @param target
260 * the target to delete
59864f77
NR
261 *
262 * @return TRUE if all files were deleted, FALSE if an error occurred
263 */
264 public static boolean deltree(File target) {
265 return deltree(target, null).isEmpty();
266 }
267
268 /**
269 * Recursively delete the given {@link File}, which may of course also be a
270 * directory.
271 * <p>
272 * Will collect all {@link File} that cannot be deleted in the given
273 * accumulator.
274 *
275 * @param target
276 * the target to delete
277 * @param errorAcc
278 * the accumulator to use for errors, or NULL to create a new one
279 *
280 * @return the errors accumulator
ec1f3444 281 */
59864f77
NR
282 public static List<File> deltree(File target, List<File> errorAcc) {
283 if (errorAcc == null) {
284 errorAcc = new ArrayList<File>();
285 }
286
7ee9568b
NR
287 File[] files = target.listFiles();
288 if (files != null) {
289 for (File file : files) {
59864f77 290 errorAcc = deltree(file, errorAcc);
ec1f3444
NR
291 }
292 }
293
294 if (!target.delete()) {
59864f77 295 errorAcc.add(target);
ec1f3444 296 }
59864f77
NR
297
298 return errorAcc;
ec1f3444 299 }
b607df60 300
16d59378
NR
301 /**
302 * Open the given /-separated resource (from the binary root).
303 *
304 * @param name
305 * the resource name
306 *
307 * @return the opened resource if found, NLL if not
308 */
309 public static InputStream openResource(String name) {
310 ClassLoader loader = IOUtils.class.getClassLoader();
311 if (loader == null) {
312 loader = ClassLoader.getSystemClassLoader();
313 }
314
315 return loader.getResourceAsStream(name);
316 }
80500544
NR
317
318 /**
319 * Return a resetable {@link InputStream} from this stream, and reset it.
320 *
321 * @param in
322 * the input stream
323 * @return the resetable stream, which <b>may</b> be the same
324 *
325 * @throws IOException
326 * in case of I/O error
327 */
328 public static InputStream forceResetableStream(InputStream in)
329 throws IOException {
330 MarkableFileInputStream tmpIn = null;
331 File tmp = null;
332
333 boolean resetable = in.markSupported();
334 if (resetable) {
335 try {
336 in.reset();
337 } catch (IOException e) {
338 resetable = false;
339 }
340 }
341
342 if (resetable) {
343 return in;
344 }
345
346 tmp = File.createTempFile(".tmp-stream", ".tmp");
347 try {
348 write(in, tmp);
349 tmpIn = new MarkableFileInputStream(new FileInputStream(tmp));
350 return tmpIn;
351 } finally {
352 try {
353 if (tmpIn != null) {
354 tmpIn.close();
355 }
356 } finally {
357 tmp.delete();
358 }
359 }
360 }
361
362 /**
363 * Convert the {@link InputStream} into a byte array.
364 *
365 * @param in
366 * the input stream
367 *
368 * @return the array
369 *
370 * @throws IOException
371 * in case of I/O error
372 */
373 public static byte[] toByteArray(InputStream in) throws IOException {
374 ByteArrayOutputStream out = new ByteArrayOutputStream();
375 write(in, out);
376
377 byte[] array = out.toByteArray();
378 out.close();
379
380 return array;
381 }
ec1f3444 382}