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