View Javadoc
1   /*
2    * SPDX-License-Identifier: Apache-2.0
3    * See LICENSE file for details.
4    *
5    * Copyright 2018-2026 Hazendaz
6    * Copyright 2011-2018 tunyk
7    */
8   package com.tunyk.mvn.plugins.htmlcompressor;
9   
10  import java.io.IOException;
11  import java.nio.charset.Charset;
12  import java.nio.file.Files;
13  import java.nio.file.Path;
14  import java.util.Arrays;
15  import java.util.List;
16  import java.util.Map;
17  import java.util.Map.Entry;
18  import java.util.concurrent.ConcurrentHashMap;
19  import java.util.concurrent.ConcurrentMap;
20  import java.util.regex.Matcher;
21  import java.util.stream.Collectors;
22  import java.util.stream.Stream;
23  
24  import org.json.JSONException;
25  import org.json.JSONObject;
26  
27  /**
28   * The Class FileTool.
29   */
30  public class FileTool {
31  
32      /** The root dir path. */
33      private String rootDirPath;
34  
35      /** The file extensions. */
36      private String[] fileExtensions;
37  
38      /** The recursive. */
39      private boolean recursive;
40  
41      /** The file encoding. */
42      private Charset fileEncoding;
43  
44      /**
45       * Instantiates a new file tool.
46       *
47       * @param rootDir
48       *            the root dir
49       * @param fileExtensions
50       *            the file ext
51       * @param recursive
52       *            the recursive
53       *
54       * @throws IOException
55       *             Signals that an I/O exception has occurred.
56       */
57      public FileTool(String rootDir, String[] fileExtensions, boolean recursive) throws IOException {
58          this.setRootDirPath(rootDir);
59          this.fileExtensions = fileExtensions;
60          this.recursive = recursive;
61      }
62  
63      /**
64       * Gets the files.
65       *
66       * @return the files
67       *
68       * @throws IOException
69       *             Signals that an I/O exception has occurred.
70       */
71      public ConcurrentMap<String, String> getFiles() throws IOException {
72          ConcurrentMap<String, String> map = new ConcurrentHashMap<>();
73          Path rootDir = Path.of(rootDirPath);
74          List<Path> paths;
75          try (Stream<Path> walk = Files.walk(rootDir)) {
76              paths = walk.map(Path::normalize).filter(Files::isRegularFile)
77                      .filter(path -> Arrays.stream(fileExtensions).anyMatch(path.getFileName().toString()::endsWith))
78                      .collect(Collectors.toList());
79          }
80          int truncationIndex = 0;
81          for (Path path : paths) {
82              String normalizedFilePath = path.toFile().getCanonicalPath().replace("\\", "/");
83              if (truncationIndex == 0) {
84                  truncationIndex = normalizedFilePath.indexOf(rootDirPath) + rootDirPath.length() + 1;
85              }
86              String key = normalizedFilePath.substring(truncationIndex);
87              String value = Files.readString(path, getFileEncoding());
88              map.put(key, value);
89          }
90          return map;
91      }
92  
93      /**
94       * Write files.
95       *
96       * @param map
97       *            the map
98       * @param targetDir
99       *            the target dir
100      *
101      * @throws IOException
102      *             Signals that an I/O exception has occurred.
103      */
104     public void writeFiles(Map<String, String> map, String targetDir) throws IOException {
105         for (Entry<String, String> entry : map.entrySet()) {
106             Path path = Path.of(targetDir + '/' + entry.getKey());
107             Files.createDirectories(path.getParent());
108             Files.writeString(path, entry.getValue(), getFileEncoding());
109         }
110     }
111 
112     /**
113      * Write to json file.
114      *
115      * @param map
116      *            the map
117      * @param targetFile
118      *            the target file
119      * @param integrationCode
120      *            the integration code
121      *
122      * @throws IOException
123      *             Signals that an I/O exception has occurred.
124      * @throws JSONException
125      *             the JSON exception
126      */
127     public void writeToJsonFile(Map<String, String> map, String targetFile, String integrationCode)
128             throws IOException, JSONException {
129         String replacePattern = "\"%s\"";
130         Path path = Path.of(targetFile);
131         JSONObject json = new JSONObject();
132         for (Entry<String, String> entry : map.entrySet()) {
133             json.put(entry.getKey(), entry.getValue());
134         }
135         if (integrationCode == null) {
136             integrationCode = replacePattern;
137         }
138         if (integrationCode.indexOf(replacePattern) == -1) {
139             integrationCode += replacePattern;
140         }
141         String contents = integrationCode.replaceFirst(replacePattern, Matcher.quoteReplacement(json.toString()));
142         Files.createDirectories(path.getParent());
143         Files.writeString(path, contents, getFileEncoding());
144     }
145 
146     /**
147      * Human readable byte count.
148      *
149      * @param bytes
150      *            the bytes
151      * @param systemOfUnits
152      *            the systemOfUnits
153      *
154      * @return the string
155      */
156     // TODO JWL 4/22/2023 Didn't see a good way to handle as it gets flagged to remove unnecessary cast if I fix this
157     // per error-prone, so ignoring it
158     @SuppressWarnings("LongDoubleConversion")
159     public static String humanReadableByteCount(long bytes, boolean systemOfUnits) {
160         int unit = systemOfUnits ? 1000 : 1024;
161         if (bytes < unit) {
162             return bytes + " B";
163         }
164         int exp = (int) (Math.log(bytes) / Math.log(unit));
165         String pre = (systemOfUnits ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (systemOfUnits ? "" : "i");
166         return "%.1f %sB".formatted(bytes / Math.pow(unit, exp), pre);
167     }
168 
169     /**
170      * Gets the elapsed HMS time.
171      *
172      * @param elapsedTime
173      *            the elapsed time
174      *
175      * @return the elapsed HMS time
176      */
177     public static String getElapsedHMSTime(long elapsedTime) {
178         String format = "%%0%dd".formatted(2);
179         elapsedTime = elapsedTime / 1000;
180         String seconds = format.formatted(elapsedTime % 60);
181         String minutes = format.formatted((elapsedTime % 3600) / 60);
182         String hours = format.formatted(elapsedTime / 3600);
183         return hours + ":" + minutes + ":" + seconds;
184     }
185 
186     /**
187      * Gets the root dir path.
188      *
189      * @return the root dir path
190      */
191     public String getRootDirPath() {
192         return rootDirPath;
193     }
194 
195     /**
196      * Sets the root dir path.
197      *
198      * @param rootDirPath
199      *            the new root dir path
200      *
201      * @throws IOException
202      *             Signals that an I/O exception has occurred.
203      */
204     public void setRootDirPath(String rootDirPath) throws IOException {
205         Path path = Path.of(rootDirPath);
206         this.rootDirPath = path.toFile().getCanonicalPath().replace("\\", "/").replaceAll("/$", "");
207     }
208 
209     /**
210      * Gets the file extensions.
211      *
212      * @return the file extensions
213      */
214     public String[] getFileExtensions() {
215         return fileExtensions;
216     }
217 
218     /**
219      * Sets the file extensions.
220      *
221      * @param fileExtensions
222      *            the new file extensions
223      */
224     public void setFileExtensions(String[] fileExtensions) {
225         this.fileExtensions = fileExtensions;
226     }
227 
228     /**
229      * Checks if is recursive.
230      *
231      * @return true, if is recursive
232      */
233     public boolean isRecursive() {
234         return recursive;
235     }
236 
237     /**
238      * Sets the recursive.
239      *
240      * @param recursive
241      *            the new recursive
242      */
243     public void setRecursive(boolean recursive) {
244         this.recursive = recursive;
245     }
246 
247     /**
248      * Gets the file encoding.
249      *
250      * @return the file encoding
251      */
252     public Charset getFileEncoding() {
253         return fileEncoding == null ? Charset.defaultCharset() : fileEncoding;
254     }
255 
256     /**
257      * Sets the file encoding.
258      *
259      * @param fileEncoding
260      *            the new file encoding
261      */
262     public void setFileEncoding(Charset fileEncoding) {
263         this.fileEncoding = fileEncoding == null ? Charset.defaultCharset() : fileEncoding;
264     }
265 }