• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package com.ohos.hapsigntool.utils;
17 
18 import com.google.gson.Gson;
19 import com.google.gson.GsonBuilder;
20 import com.ohos.hapsigntool.error.ERROR;
21 
22 import org.apache.logging.log4j.LogManager;
23 import org.apache.logging.log4j.Logger;
24 
25 import java.io.BufferedWriter;
26 import java.io.ByteArrayOutputStream;
27 import java.io.Closeable;
28 import java.io.DataOutputStream;
29 import java.io.File;
30 import java.io.FileInputStream;
31 import java.io.FileNotFoundException;
32 import java.io.FileOutputStream;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.OutputStream;
36 import java.io.OutputStreamWriter;
37 import java.nio.charset.Charset;
38 import java.nio.file.Files;
39 import java.util.HashMap;
40 import java.util.Map;
41 import java.util.regex.Matcher;
42 import java.util.regex.Pattern;
43 
44 /**
45  * Common file operation.
46  *
47  * @since 2021/12/28
48  */
49 public final class FileUtils {
50     /**
51      * LOGGER.
52      */
53     private static final Logger LOGGER = LogManager.getLogger(FileUtils.class);
54 
55     /**
56      * suffix regex map
57      */
58     public static final Map<String, Pattern> SUFFIX_REGEX_MAP = new HashMap<>();
59 
60     /**
61      * add GSON static.
62      */
63     public static final Gson GSON = (new GsonBuilder()).disableHtmlEscaping().create();
64 
65     /**
66      * add GSON_PRETTY_PRINT static.
67      */
68     public static final Gson GSON_PRETTY_PRINT = (new GsonBuilder()).disableHtmlEscaping().setPrettyPrinting().create();
69 
70     /**
71      * File reader block size
72      */
73     public static final int FILE_BUFFER_BLOCK = 1024 * 1024;
74 
75     /**
76      * File end
77      */
78     public static final int FILE_END = -1;
79 
80     /**
81      * Expected split string length
82      */
83     public static final int SPLIT_LENGTH = 2;
84 
85     private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
86 
87     static {
88         SUFFIX_REGEX_MAP.put("so", Pattern.compile("\\.so(\\.[0-9]*){0,3}$"));
89     }
90 
FileUtils()91     private FileUtils() {
92     }
93 
94     /**
95      * Close closeable quietly.
96      *
97      * @param closeable closeable
98      */
close(Closeable closeable)99     public static void close(Closeable closeable) {
100         if (closeable != null) {
101             try {
102                 closeable.close();
103             } catch (IOException exception) {
104                 LOGGER.debug(exception.getMessage(), exception);
105             }
106         }
107     }
108 
109     /**
110      * Read byte from input file.
111      *
112      * @param file Which file to read
113      * @return byte content
114      * @throws IOException Read failed
115      */
readFile(File file)116     public static byte[] readFile(File file) throws IOException {
117         return read(Files.newInputStream(file.toPath()));
118     }
119 
120     /**
121      * Read byte from input stream.
122      *
123      * @param input Input stream
124      * @return File content
125      * @throws IOException Read failed
126      */
read(InputStream input)127     public static byte[] read(InputStream input) throws IOException {
128         try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
129             byte[] buffer = new byte[FILE_BUFFER_BLOCK];
130             int read;
131             while ((read = input.read(buffer)) != FILE_END) {
132                 output.write(buffer, 0, read);
133             }
134             return output.toByteArray();
135         } finally {
136             close(input);
137         }
138     }
139 
140     /**
141      * Read byte from input file.
142      *
143      * @param file input file
144      * @param offset offset
145      * @param length length
146      * @return data bytes
147      */
readFileByOffsetAndLength(File file, long offset, long length)148     public static byte[] readFileByOffsetAndLength(File file, long offset, long length) throws IOException {
149         try (FileInputStream input = new FileInputStream(file)) {
150             return readInputByOffsetAndLength(input, offset, length);
151         }
152     }
153 
154     /**
155      * Read byte from input stream.
156      *
157      * @param input input stream
158      * @param offset offset
159      * @param length length
160      * @return data bytes
161      * @throws IOException read exception
162      */
readInputByOffsetAndLength(InputStream input, long offset, long length)163     public static byte[] readInputByOffsetAndLength(InputStream input, long offset, long length) throws IOException {
164         input.skip(offset);
165         return readInputByLength(input, length);
166     }
167 
168     /**
169      * Read byte from input stream.
170      *
171      * @param input InputStream
172      * @param length length
173      * @return data bytes
174      */
readInputByLength(InputStream input, long length)175     public static byte[] readInputByLength(InputStream input, long length) throws IOException {
176         try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
177             if (length > Integer.MAX_VALUE) {
178                 throw new IOException("Size cannot be greater than Integer max value: " + length);
179             }
180             writeInputToOutPut(input, output, length);
181             return output.toByteArray();
182         }
183     }
184 
185     /**
186      * write input to output by length
187      */
writeInputToOutPut(InputStream input, OutputStream output, long length)188     private static void writeInputToOutPut(InputStream input, OutputStream output, long length) throws IOException {
189         byte[] buffer = new byte[FILE_BUFFER_BLOCK];
190         long hasReadLen = 0L;
191         while (hasReadLen < length) {
192             int readLen = (int) Math.min(length - hasReadLen, FILE_BUFFER_BLOCK);
193             int len = input.read(buffer, 0, readLen);
194             if (len != readLen) {
195                 throw new IOException("read" + hasReadLen + "bytes data less than " + length);
196             }
197             output.write(buffer, 0, len);
198             hasReadLen += len;
199         }
200     }
201 
202     /**
203      * Out put content to file.
204      *
205      * @param content Which content to out put
206      * @param output  File to write
207      * @throws IOException Write failed
208      */
write(byte[] content, File output)209     public static void write(byte[] content, File output) throws IOException {
210         try (FileOutputStream out = new FileOutputStream(output)) {
211             for (byte con : content) {
212                 out.write(con);
213             }
214         }
215     }
216 
217     /**
218      * Write data in file to output stream
219      *
220      * @param inFile input file path.
221      * @param out output file path.
222      * @param offset file read offset
223      * @param size file read size
224      * @return true, if write successfully.
225      */
appendWriteFileByOffsetToFile(String inFile, FileOutputStream out, long offset, long size)226     public static boolean appendWriteFileByOffsetToFile(String inFile, FileOutputStream out, long offset, long size) {
227         File inputFile = new File(inFile);
228         try (FileInputStream fis = new FileInputStream(inputFile)) {
229             fis.skip(offset);
230             writeInputToOutPut(fis, out, size);
231             return true;
232         } catch (FileNotFoundException e) {
233             LOGGER.error("Failed to get input stream object.");
234         } catch (IOException e) {
235             LOGGER.error("Failed to read or write data.");
236         }
237         return false;
238     }
239 
240     /**
241      * Check file exist or not.
242      *
243      * @param filePath File path
244      * @return Is file exist
245      */
isFileExist(String filePath)246     public static boolean isFileExist(String filePath) {
247         return new File(filePath).exists();
248     }
249 
250     /**
251      * Throw runtime exception if not allowed file type.
252      *
253      * @param filePath file path
254      * @param types    Such as "txt" "json" "mp3"
255      */
validFileType(String filePath, String... types)256     public static void validFileType(String filePath, String... types) {
257         String suffix = getSuffix(filePath);
258         ValidateUtils.throwIfNotMatches(!StringUtils.isEmpty(suffix),
259                 ERROR.NOT_SUPPORT_ERROR, "Not support file: " + filePath);
260         boolean isMatches = false;
261         for (String type : types) {
262             if (StringUtils.isEmpty(type)) {
263                 continue;
264             }
265             if (type.equalsIgnoreCase(suffix)) {
266                 isMatches = true;
267                 break;
268             }
269         }
270         ValidateUtils.throwIfNotMatches(isMatches, ERROR.NOT_SUPPORT_ERROR, "Not support file: " + filePath);
271     }
272 
273     /**
274      * Get suffix of file.
275      *
276      * @param filePath file path
277      * @return file suffix. Such as "txt" "json" "p12"
278      */
getSuffix(String filePath)279     public static String getSuffix(String filePath) {
280         if (StringUtils.isEmpty(filePath)) {
281             return "";
282         }
283         File file = new File(filePath);
284         String fileName = file.getName();
285         String[] temps = fileName.split("\\.");
286         if (temps.length < SPLIT_LENGTH) {
287             return "";
288         }
289         return temps[temps.length - 1];
290     }
291 
292     /**
293      * Write data in file to output stream
294      *
295      * @param file input file path.
296      * @param dos output stream.
297      * @return true, if write successfully.
298      */
writeFileToDos(String file, DataOutputStream dos)299     public static boolean writeFileToDos(String file, DataOutputStream dos) {
300         if (dos == null) {
301             return false;
302         }
303         File src = new File(file);
304         try (FileInputStream fileStream = new FileInputStream(src)) {
305             int temp;
306             byte[] buf = new byte[FILE_BUFFER_BLOCK];
307             while ((temp = fileStream.read(buf)) > 0) {
308                 dos.write(buf, 0, temp);
309             }
310             return true;
311         } catch (FileNotFoundException e) {
312             LOGGER.error("Failed to get input stream object.");
313         } catch (IOException e) {
314             LOGGER.error("Failed to read or write data.");
315         }
316         return false;
317     }
318 
319     /**
320      * Write byte array to output stream
321      *
322      * @param data byte array
323      * @param dos output stream
324      * @return true, if write successfully.
325      */
writeByteToDos(byte[] data, DataOutputStream dos)326     public static boolean writeByteToDos(byte[] data, DataOutputStream dos) {
327         if (data == null) {
328             return true;
329         }
330         try {
331             dos.write(data);
332         } catch (IOException e) {
333             LOGGER.error("Failed to write data to output stream.");
334             return false;
335         }
336         return true;
337     }
338 
339     /**
340      * Get length of file.
341      *
342      * @param filePath input file path.
343      * @return long value of file length.
344      */
getFileLen(String filePath)345     public static long getFileLen(String filePath) {
346         File file = new File(filePath);
347         if (file.exists() && file.isFile()) {
348             return file.length();
349         }
350         return -1;
351     }
352 
353     /**
354      * Write byte array data to output file.
355      *
356      * @param bytes byte array data.
357      * @param outFile output file path.
358      * @return true, if write successfully.
359      */
writeByteToOutFile(byte[] bytes, String outFile)360     public static boolean writeByteToOutFile(byte[] bytes, String outFile) {
361         try (OutputStream ops = new FileOutputStream(outFile, true)) {
362             return writeByteToOutFile(bytes, ops);
363         } catch (FileNotFoundException e) {
364             LOGGER.error("Failed to get output stream object, outfile: " + outFile);
365         } catch (IOException e) {
366             LOGGER.error("Failed to write data to ops, outfile: " + outFile);
367         }
368         return false;
369     }
370 
371     /**
372      * Write byte array data to output file.
373      *
374      * @param bytes byte array data.
375      * @param outFile output file path.
376      * @return true, if write successfully.
377      */
writeByteToOutFile(byte[] bytes, OutputStream outFile)378     public static boolean writeByteToOutFile(byte[] bytes, OutputStream outFile) {
379         try {
380             outFile.write(bytes, 0, bytes.length);
381             outFile.flush();
382             return true;
383         } catch (FileNotFoundException e) {
384             LOGGER.error("Failed to get output stream object, outfile: " + outFile);
385         } catch (IOException e) {
386             LOGGER.error("Failed to write data to ops, outfile: " + outFile);
387         }
388         return false;
389     }
390 
391     /**
392      * Check input file is valid.
393      *
394      * @param file input file.
395      * @throws IOException file is a directory or can't be read.
396      */
isValidFile(File file)397     public static void isValidFile(File file) throws IOException {
398         if (!file.exists()) {
399             throw new FileNotFoundException("File '" + file + "' does not exist");
400         }
401 
402         if (file.isDirectory()) {
403             throw new IOException("File '" + file + "' exists but is a directory");
404         }
405 
406         if (!file.canRead()) {
407             throw new IOException("File '" + file + "' cannot be read");
408         }
409     }
410 
411     /**
412      * Open an input stream of input file safely.
413      *
414      * @param file input file.
415      * @return an input stream of input file
416      * @throws IOException file is a directory or can't be read.
417      */
openInputStream(File file)418     public static FileInputStream openInputStream(File file) throws IOException {
419         isValidFile(file);
420         return new FileInputStream(file);
421     }
422 
toByteArray(final InputStream input, final int size)423     private static byte[] toByteArray(final InputStream input, final int size) throws IOException {
424         if (size < 0) {
425             throw new IllegalArgumentException("Size must be equal or greater than zero: " + size);
426         }
427 
428         if (size == 0) {
429             return EMPTY_BYTE_ARRAY;
430         }
431 
432         final byte[] data = new byte[size];
433         int offset = 0;
434         int read;
435 
436         while (offset < size && (read = input.read(data, offset, size - offset)) != FILE_END) {
437             offset += read;
438         }
439 
440         if (offset != size) {
441             throw new IOException("Unexpected read size. current: " + offset + ", expected: " + size);
442         }
443 
444         return data;
445     }
446 
447     /**
448      * Read file to byte array.
449      *
450      * @param file input file.
451      * @return byte array of file-content.
452      * @throws IOException fill exception
453      */
readFileToByteArray(File file)454     public static byte[] readFileToByteArray(File file) throws IOException {
455         try (InputStream in = openInputStream(file)) {
456             final long fileLength = file.length();
457             if (fileLength > Integer.MAX_VALUE) {
458                 throw new IllegalArgumentException("Size cannot be greater than Integer max value: " + fileLength);
459             }
460             return toByteArray(in, (int) fileLength);
461         }
462     }
463 
464     /**
465      * Write a string to file
466      *
467      * @param source String will be written.
468      * @param filePath path to write file.
469      * @param charset a charset
470      * @throws IOException write error.
471      */
writeStringToFile(String source, String filePath, Charset charset)472     public static void writeStringToFile(String source, String filePath, Charset charset) throws IOException {
473         try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
474                 new FileOutputStream(filePath), charset))) {
475             writer.write(source);
476             writer.flush();
477         }
478     }
479 
480     /**
481      * Delete a file quietly
482      *
483      * @param file the file to delete
484      */
deleteFile(File file)485     public static void deleteFile(File file) {
486         if (file != null && file.isFile()) {
487             try {
488                 Files.delete(file.toPath());
489             } catch (IOException e) {
490                 LOGGER.warn("delete file '{}' error, error message: {}", file, e.getMessage());
491             }
492         }
493     }
494 
495     /**
496      * regex filename
497      *
498      * @param name filename
499      * @return boolean
500      */
isRunnableFile(String name)501     public static boolean isRunnableFile(String name) {
502         if (StringUtils.isEmpty(name)) {
503             return false;
504         }
505         if (name.endsWith(".an") || name.endsWith(".abc")) {
506             return true;
507         }
508         for (Pattern pattern : SUFFIX_REGEX_MAP.values()) {
509             Matcher matcher = pattern.matcher(name);
510             if (matcher.find()) {
511                 return true;
512             }
513         }
514         return false;
515     }
516 }
517