aboutsummaryrefslogtreecommitdiffstats
path: root/vespajlib/src/main/java/com/yahoo/io/IOUtils.java
blob: 699ac22d2781218472dd8709096f6cfa146f6add (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.util.List;
import java.nio.charset.Charset;
import java.nio.ByteBuffer;

/**
 * Some static io convenience methods.
 *
 * @author bratseth
 * @author Bjorn Borud
 */
public abstract class IOUtils {

    static private final Charset utf8Charset = StandardCharsets.UTF_8;

    /** Closes a writer, or does nothing if the writer is null */
    public static void closeWriter(Writer writer) {
        if (writer == null) return;
        try { writer.close(); } catch (IOException e) {}
    }

    /** Closes a reader, or does nothing if the reader is null */
    public static void closeReader(Reader reader) {
        if (reader == null) return;
        try { reader.close(); } catch (IOException e) {}
    }

    /** Closes an input stream, or does nothing if the stream is null */
    public static void closeInputStream(InputStream stream) {
        if (stream == null) return;
        try { stream.close(); } catch (IOException e) {}
    }

    /** Closes an output stream, or does nothing if the stream is null */
    public static void closeOutputStream(OutputStream stream) {
        if (stream == null) return;
        try { stream.close(); } catch (IOException e) {}
    }

    /**
     * Creates a buffered reader
     *
     * @param filename the name or path of the file
     * @param encoding the encoding of the file, for instance "UTF-8"
     */
    public static BufferedReader createReader(File filename, String encoding) throws IOException {
        return new BufferedReader(new InputStreamReader(new FileInputStream(filename), encoding));
    }

    /**
     * Creates a buffered reader
     *
     * @param filename the name or path of the file
     * @param encoding the encoding of the file, for instance "UTF-8"
     */
    public static BufferedReader createReader(String filename, String encoding) throws IOException {
        return new BufferedReader(new InputStreamReader(new FileInputStream(filename), encoding));
    }

    /** Creates a buffered reader in the default encoding */
    public static BufferedReader createReader(String filename) throws IOException {
        return new BufferedReader(new FileReader(filename));
    }

    /**
     * Creates a buffered writer,
     * and the directories to contain it if they do not exist
     *
     * @param filename the name or path of the file
     * @param encoding the encoding to use, for instance "UTF-8"
     * @param append whether to append to the files if it exists
     */
    public static BufferedWriter createWriter(String filename, String encoding, boolean append) throws IOException {
        createDirectory(filename);
        return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename, append), encoding));
    }

    /**
     * Creates a buffered writer,
     * and the directories to contain it if they do not exist
     *
     * @param file the file to write to
     * @param encoding the encoding to use, for instance "UTF-8"
     * @param append whether to append to the files if it exists
     */
    public static BufferedWriter createWriter(File file, String encoding, boolean append) throws IOException {
        createDirectory(file.getAbsolutePath());
        return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append),encoding));
    }

    /**
     * Creates a buffered writer in the default encoding
     *
     * @param filename the name or path of the file
     * @param append whether to append to the files if it exists
     */
    public static BufferedWriter createWriter(String filename, boolean append) throws IOException {
        createDirectory(filename);
        return new BufferedWriter(new FileWriter(filename, append));
    }

    /**
     * Creates a buffered writer in the default encoding
     *
     * @param file the file to write to
     * @param append whether to append to the files if it exists
     */
    public static BufferedWriter createWriter(File file, boolean append) throws IOException {
        createDirectory(file.getAbsolutePath());
        return new BufferedWriter(new FileWriter(file, append));
    }

    /** Creates the directory path of this file if it does not exist */
    public static void createDirectory(String filename) {
        File directory = new File(filename).getParentFile();

        if (directory != null)
            directory.mkdirs();
    }

    /**
     * Copies the n first lines of a file to another file.
     * If the out file exists it will be overwritten
     *
     * @throws IOException if copying fails
     */
    public static void copy(String inFile, String outFile, int lineCount) throws IOException {
        BufferedReader reader = null;
        BufferedWriter writer = null;

        try {
            reader = createReader(inFile);
            writer = createWriter(outFile, false);
            int c;

            int newLines = 0;
            while (-1 != (c=reader.read()) && newLines<lineCount) {
                writer.write(c);
                if (c=='\n')
                    newLines++;
            }
        } finally {
            closeReader(reader);
            closeWriter(writer);
        }
    }

    /**
     * Copies a file to another file.
     * If the out file exists it will be overwritten.
     *
     * @throws IOException if copying fails
     */
    public static void copy(String inFile, String outFile) throws IOException {
        copy(new File(inFile), new File(outFile));
    }

    /**
     * Copies a file to another file.
     * If the out file exists it will be overwritten.
     */
    public static void copy(File inFile, File outFile) throws IOException {
        try (FileChannel sourceChannel = new FileInputStream(inFile).getChannel();
             FileChannel destChannel = new FileOutputStream(outFile).getChannel()) {
            destChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
        }
    }

    /**
     * Copies all files and subdirectories in a directory to another.
     * Any existing files are overwritten.
     *
     * @param sourceLocation the source directory
     * @param targetLocation the target directory
     * @param maxRecurseLevel if this is 1, only files immediately in sourceLocation are copied,
     *        if it is 2, then files contained in immediate subdirectories are copied, etc.
     *        If it is 0, sourceLocation will only be copied if it is a file, not a directory.
     *        If it is negative, recursion is infinite.
     * @throws IOException if copying any file fails. This will typically result in some files being copied and
     *         others not, i.e this method is not exception safe
     */
    public static void copyDirectory(File sourceLocation , File targetLocation, int maxRecurseLevel) throws IOException {
        copyDirectory(sourceLocation, targetLocation, maxRecurseLevel, new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return true;
            }
        });
    }

    /**
     * Copies all files and subdirectories in a directory to another.
     * Any existing files are overwritten.
     *
     * @param sourceLocation the source directory
     * @param targetLocation the target directory
     * @param maxRecurseLevel if this is 1, only files immediately in sourceLocation are copied,
     *        if it is 2, then files contained in immediate subdirectories are copied, etc.
     *        If it is 0, sourceLocation will only be copied if it is a file, not a directory.
     *        If it is negative, recursion is infinite.
     * @param filter Only copy files passing through filter.
     * @throws IOException if copying any file fails. This will typically result in some files being copied and
     *         others not, i.e this method is not exception safe
     */
    public static void copyDirectory(File sourceLocation, File targetLocation, int maxRecurseLevel, FilenameFilter filter) throws IOException {
        if ( ! sourceLocation.exists()) throw new IllegalArgumentException(sourceLocation.getAbsolutePath() + " does not exist");

        if ( ! sourceLocation.isDirectory()) { // copy file
            InputStream in=null;
            OutputStream out=null;
            try {
                in = new FileInputStream(sourceLocation);
                out = new FileOutputStream(targetLocation);
                // Copy the bits from instream to outstream
                byte[] buf = new byte[1024];
                int len;
                while ((len = in.read(buf)) > 0) {
                    out.write(buf, 0, len);
                }
            }
            finally {
                closeInputStream(in);
                closeOutputStream(out);
            }
        }
        else if (maxRecurseLevel!=0) { // copy directory if allowed
            if (!targetLocation.exists())
                targetLocation.mkdirs();

            String[] children = sourceLocation.list(filter);
            for (int i=0; i<children.length; i++)
                copyDirectory(new File(sourceLocation, children[i]),
                              new File(targetLocation, children[i]),
                              maxRecurseLevel-1);
        }
    }

    /**
     * Copies all files and subdirectories (infinitely recursively) in a directory to another.
     * Any existing files are overwritten.
     *
     * @param sourceLocation the source directory
     * @param targetLocation the target directory
     * @throws IOException if copying any file fails. This will typically result in some files being copied and
     *         others not, i.e this method is not exception safe
     */
    public static void copyDirectory(File sourceLocation , File targetLocation) throws IOException {
        copyDirectory(sourceLocation, targetLocation, -1);
    }

    /**
     * Copies the whole source directory (infinitely recursively) into the target directory.
     * @throws IOException if copying any file fails. This will typically result in some files being copied and
     *         others not, i.e this method is not exception safe
     */
    public static void copyDirectoryInto(File sourceLocation, File targetLocation) throws IOException {
        File destination = new File(targetLocation, sourceLocation.getAbsoluteFile().getName());
        copyDirectory(sourceLocation, destination);
    }

    /**
     * Returns the number of lines in a file.
     * If the file does not exists, 0 is returned
     */
    public static int countLines(String file) {
        BufferedReader reader = null;
        int lineCount = 0;

        try {
            reader = createReader(file,"utf8");
            while (reader.readLine() != null)
                lineCount++;
            return lineCount;
        } catch (IOException e) {
            return lineCount;
        } finally {
            closeReader(reader);
        }
    }

    /**
     * Returns a list containing the lines in the given file as strings
     *
     * @return a list of Strings for the lines of the file, in order
     * @throws IOException if the file could not be read
     */
    public static List<String> getLines(String fileName) throws IOException {
        BufferedReader reader = null;

        try {
            List<String> lines = new java.util.ArrayList<>();

            reader = createReader(fileName,"utf8");
            String line;

            while (null != (line = reader.readLine()))
                lines.add(line);
            return lines;
        } finally {
            closeReader(reader);
        }
    }

    /**
     * Recursive deletion of directories
     */
    public static boolean recursiveDeleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();

            for (String child : children) {
                boolean success = recursiveDeleteDir(new File(dir, child));

                if (!success) return false;
            }
        }

        // The directory is now empty so delete it
        return dir.delete();
    }

    /**
     * Encodes string as UTF-8 into ByteBuffer
     */
    public static ByteBuffer utf8ByteBuffer(String s) {
        return utf8Charset.encode(s);
    }

    /**
     * Reads the contents of a UTF-8 text file into a String.
     *
     * @param file the file to read, or null
     * @return the file content as a string, or null if the input file was null
     */
    public static String readFile(File file) throws IOException {
        try {
            if (file == null) return null;
            return Files.readString(file.toPath(), utf8Charset);
        }
        catch (NoSuchFileException e) {
            throw new NoSuchFileException("Could not find file '" + file.getAbsolutePath() + "'");
        }
    }

    /**
     * Reads all the content of the given input stream, in chunks of at max chunkSize
     */
    public static byte[] readBytes(InputStream stream, int chunkSize) throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();

        int nRead;
        byte[] data = new byte[chunkSize];
        while ((nRead = stream.read(data, 0, data.length)) != -1)
            buffer.write(data, 0, nRead);
        buffer.flush();
        return buffer.toByteArray();
    }

    /**
     * Reads the content of a file into a byte array
     */
    public static byte[] readFileBytes(File file) throws IOException {
        long lengthL = file.length();
        if (lengthL>Integer.MAX_VALUE)
            throw new IllegalArgumentException("File too big for byte array: "+file.getCanonicalPath());

        try (InputStream in = new FileInputStream(file)) {
            int length = (int)lengthL;
            byte[] array = new byte[length];
            int offset = 0;
            int count=0;
            while (offset < length && (count = in.read(array, offset, (length - offset)))>=0)
                offset += count;
            return array;
        }
    }

    /**
     * Reads all data from a reader into a string. Uses a buffer to speed up reading.
     */
    public static String readAll(Reader reader) throws IOException {
        StringBuilder sb = new StringBuilder();
        try (BufferedReader buffered = new BufferedReader(reader)) {
            int c;
            while ((c = buffered.read()) != -1)
                sb.appendCodePoint(c);
        }
        return sb.toString();
    }

    /** Read an input stream completely into a string */
    public static String readAll(InputStream stream, Charset charset) throws IOException {
        return new String(stream.readAllBytes(), charset);
    }

    /** Convenience method for closing a list of readers. Does nothing if the given reader list is null. */
    public static void closeAll(List<Reader> readers) {
        if (readers==null) return;
        for (Reader reader : readers)
            closeReader(reader);
    }

    /**
     * Writes the given string to the file
     */
    public static void writeFile(File file, String text, boolean append) throws IOException {
       BufferedWriter out = null;
       try {
           out = createWriter(file, append);
           out.write(text);
       }
       finally {
           closeWriter(out);
       }
    }

    /** Writes the given content to the file (replacing any existing content) */
    public static void writeFile(File file, byte[] content) throws UncheckedIOException {
        try (FileOutputStream out = new FileOutputStream(file)) {
            out.write(content);
        }
        catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    /**
     * Writes the given string to the file
     */
    public static void writeFile(String file, String text, boolean append) throws IOException {
        writeFile(new File(file), text, append);
     }

}