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