1 /* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 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 * http://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 17 package ohos; 18 19 import com.alibaba.fastjson.JSON; 20 import com.alibaba.fastjson.serializer.SerializerFeature; 21 22 import java.io.File; 23 import java.io.FileInputStream; 24 import java.io.IOException; 25 import java.io.InputStream; 26 import java.math.BigInteger; 27 import java.security.MessageDigest; 28 import java.security.NoSuchAlgorithmException; 29 import java.text.SimpleDateFormat; 30 import java.util.ArrayList; 31 import java.util.List; 32 33 /** 34 * scanDuplicate info 35 * 36 * @since 2023/11/27 37 */ 38 39 public class ScanStatDuplicate { 40 private static final String UNPACK_NAME = "unpack"; 41 private static final String DUPLICATE_FOLDER_NAME = "duplicate"; 42 private static final String EMPTY_STRING = ""; 43 private static final String SHA_256 = "SHA-256"; 44 private static final String RESULT_MD5 = "md5"; 45 private static final String RESULT_SIZE = "size"; 46 private static final String RESULT_FILES = "files"; 47 private static final String DUPLICATE_DESC = "find the duplicated files"; 48 private static final String DUPLICATE_PARAM = "--stat-duplicate"; 49 private static final String TASK_TYPE = "taskType"; 50 private static final String TASK_DESC = "taskDesc"; 51 private static final String PARAM = "param"; 52 private static final String START_TIME = "startTime"; 53 private static final String STOP_TIME = "stopTime"; 54 private static final String RESULT = "result"; 55 private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss:SSS"; 56 private static final int MD5_BUFFER_SIZE = 1024; 57 private static final long DUPLICATE_TYPE = 1L; 58 private static final int MD5_LENGTH = 16; 59 private static final long SHOW_SIZE = 10L; 60 private static final String HTML_BUTTON_SHOW = "<button id=\"show_%s\" type=\"button\" " 61 + "onclick=\"show_%s()\" style=\"display: block\">more</button>"; 62 private static final String HTML_BUTTON_HIDE = "<button id=\"hide_%s\" type=\"button\" " 63 + "onclick=\"hide_%s()\" style=\"display: none\">close</button>"; 64 private static final String HTML_TABLE_BOX = "<table class=\"boxTable\">"; 65 private static final String HTML_TABLE_END = "</table>"; 66 private static final String HTML_TR_TD_LAYOUT = "<tr class=\"layout\"><td class=\"key\">"; 67 private static final String HTML_TR_TD_VALUE = "</td><td class=\"value\">"; 68 private static final String HTML_TR_TD_END = "</td></tr>"; 69 private static final String HTML_TR_TD_RESULT = "<tr class=\"result\"><td class=\"key\">"; 70 private static final String HTML_TR_TD = "<tr%s><td%s>"; 71 private static final String HTML_TD_END_TD = "</td><td%s>"; 72 private static final String HTML_LI_CIRCLE = "<li type=\"circle\">"; 73 private static final String HTML_LI_END = "</li>"; 74 private static final String HTML_TR_TD_DUPLICATE_KEY = "<tr class=\"duplicateLayout\"><td class=\"duplicateKey\">"; 75 private static final String HTML_TD_DUPLICATE_VALUE = "</td><td class=\"duplicateValue\"><ul>"; 76 private static final String HTML_UL_TD_TR_END = "</ul></td></tr>"; 77 private static final String HTML_UL_HEAD = "<ul>"; 78 private static final String CLASS_DUPLICATE_LAYOUT = " class=\"duplicateLayout\""; 79 private static final String CLASS_DUPLICATE_KEY = " class=\"duplicateKey\""; 80 private static final String CLASS_DUPLICATE_VALUE = " class=\"duplicateValue\""; 81 private static final String HTML_LI_HEAD = "<li>"; 82 private static final String HTML_LI_DUPLICATE = "<li class=\"duplicate\">"; 83 private static final String HTML_TABLE_DUPLICATE = "<table class=\"duplicateTable\">"; 84 private static final String HTML_TABLE_LI_END = "</table></li>"; 85 private static final String HTML_UL_END = "</ul>"; 86 private static final Log LOG = new Log(ScanStatDuplicate.class.toString()); 87 88 private static class ParamModel { 89 private String md5 = EMPTY_STRING; 90 private long size; 91 private List<String> files = new ArrayList<>(); 92 getMd5()93 public String getMd5() { 94 return this.md5; 95 } setMd5(String md5)96 public void setMd5(String md5) { 97 this.md5 = md5; 98 } getSize()99 public long getSize() { 100 return this.size; 101 } setSize(long size)102 public void setSize(long size) { 103 this.size = size; 104 } getFiles()105 public List<String> getFiles() { 106 return this.files; 107 } setFiles(List<String> files)108 public void setFiles(List<String> files) { 109 this.files = files; 110 } 111 } 112 113 private static class DuplicateResult { 114 private long taskType = DUPLICATE_TYPE; 115 private String taskDesc = DUPLICATE_DESC; 116 private String param = DUPLICATE_PARAM; 117 private String startTime = EMPTY_STRING; 118 private String stopTime = EMPTY_STRING; 119 private List<ParamModel> result = new ArrayList<>(); 120 getTaskType()121 public long getTaskType() { 122 return this.taskType; 123 } setTaskType(long taskType)124 public void setTaskType(long taskType) { 125 this.taskType = taskType; 126 } getTaskDesc()127 public String getTaskDesc() { 128 return this.taskDesc; 129 } setTaskDesc(String taskDesc)130 public void setTaskDesc(String taskDesc) { 131 this.taskDesc = taskDesc; 132 } getParam()133 public String getParam() { 134 return this.param; 135 } setParam(String param)136 public void setParam(String param) { 137 this.param = param; 138 } getStartTime()139 public String getStartTime() { 140 return this.startTime; 141 } setStartTime(String startTime)142 public void setStartTime(String startTime) { 143 this.startTime = startTime; 144 } getStopTime()145 public String getStopTime() { 146 return this.stopTime; 147 } setStopTime(String stopTime)148 public void setStopTime(String stopTime) { 149 this.stopTime = stopTime; 150 } getResult()151 public List<ParamModel> getResult() { 152 return this.result; 153 } setResult(List<ParamModel> result)154 public void setResult(List<ParamModel> result) { 155 this.result = result; 156 } 157 } 158 159 /** 160 * scan statDuplicate. 161 * 162 * @param utility utility data 163 * @param jsonList List<String> data 164 * @param fileList List<String> data 165 * @return duplicate html 166 * @throws IOException Throws this exception if getDuplicateResList exception 167 * @throws NoSuchAlgorithmException Throws this exception if getDuplicateResList exception 168 */ statDuplicate(Utility utility, List<String> jsonList, List<String> fileList)169 public String statDuplicate(Utility utility, List<String> jsonList, List<String> fileList) 170 throws IOException, NoSuchAlgorithmException { 171 DuplicateResult duplicateResult = new DuplicateResult(); 172 duplicateResult.setStartTime(getCurrentTime()); 173 List<ParamModel> resList = getDuplicateResList(fileList); 174 File parentFile = new File(utility.getOutPath()); 175 if (!parentFile.exists() && !parentFile.mkdirs()) { 176 LOG.error(ScanErrorEnum.STAT_DUPLICATE_CREATE_FILE_ERROR.toString()); 177 } 178 List<ParamModel> filterList = new ArrayList<>(); 179 for (ParamModel model : resList) { 180 List<String> files = model.getFiles(); 181 if (files.size() > 1) { 182 filterList.add(model); 183 } 184 } 185 duplicateResult.setResult(filterList); 186 duplicateResult.setStopTime(getCurrentTime()); 187 String taskTypeHtml = getHtmlRow(TASK_TYPE, duplicateResult.getTaskType()); 188 String taskDescHtml = getHtmlRow(TASK_DESC, duplicateResult.getTaskDesc()); 189 String paramHtml = getHtmlRow(PARAM, duplicateResult.getParam()); 190 String startTimeHtml = getHtmlRow(START_TIME, duplicateResult.getStartTime()); 191 String stopTimeHtml = getHtmlRow(STOP_TIME, duplicateResult.getStopTime()); 192 String resultValue = getResultHtml(duplicateResult.getResult()); 193 String resultHtml = getHtmlRowResultClass(RESULT, resultValue); 194 String htmlStr = HTML_TABLE_BOX + taskTypeHtml + taskDescHtml + paramHtml 195 + startTimeHtml + stopTimeHtml + resultHtml + HTML_TABLE_END; 196 String jsonStr = JSON.toJSONString(duplicateResult, new SerializerFeature[] { 197 SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, 198 SerializerFeature.WriteDateUseDateFormat}); 199 jsonList.add(jsonStr); 200 return htmlStr; 201 } 202 getCurrentTime()203 private static String getCurrentTime() { 204 long currentTimeMillis = System.currentTimeMillis(); 205 return new SimpleDateFormat(DATE_FORMAT).format(currentTimeMillis); 206 } 207 getDuplicateResList(List<String> fileList)208 private List<ParamModel> getDuplicateResList(List<String> fileList) throws IOException, NoSuchAlgorithmException { 209 List<ParamModel> resList = new ArrayList<>(); 210 for (String filePath : fileList) { 211 boolean addFlag = true; 212 String md5 = md5HashCode(filePath); 213 for (ParamModel element : resList) { 214 String eleMd5 = element.getMd5(); 215 if (eleMd5.equals(md5)) { 216 List<String> eleFiles = element.getFiles(); 217 eleFiles.add(splitPath(filePath, UNPACK_NAME)); 218 element.setFiles(eleFiles); 219 addFlag = false; 220 } 221 } 222 if (addFlag) { 223 ParamModel model = new ParamModel(); 224 long size = FileUtils.getFileSize(filePath); 225 model.setMd5(md5); 226 model.setSize(size); 227 List<String> files = model.getFiles(); 228 files.add(splitPath(filePath, UNPACK_NAME)); 229 resList.add(model); 230 } 231 } 232 return resList; 233 } 234 getHtmlRow(String key, long valve)235 private static String getHtmlRow(String key, long valve) { 236 return HTML_TR_TD_LAYOUT + key + HTML_TR_TD_VALUE + valve + HTML_TR_TD_END; 237 } 238 getHtmlRow(String key, String valve)239 private static String getHtmlRow(String key, String valve) { 240 return HTML_TR_TD_LAYOUT + key + HTML_TR_TD_VALUE + valve + HTML_TR_TD_END; 241 } 242 getHtmlRowResultClass(String key, String valve)243 private static String getHtmlRowResultClass(String key, String valve) { 244 return HTML_TR_TD_RESULT + key + HTML_TR_TD_VALUE + valve + HTML_TR_TD_END; 245 } 246 getHtmlRowResult(String key, long valve, String trClass, String tdClassKey, String tdClassValue)247 private static String getHtmlRowResult(String key, long valve, 248 String trClass, String tdClassKey, String tdClassValue) { 249 return String.format(HTML_TR_TD + key + HTML_TD_END_TD + valve + HTML_TR_TD_END, 250 trClass, tdClassKey, tdClassValue); 251 } 252 getHtmlRowResult(String key, String valve, String trClass, String tdClassKey, String tdClassValue)253 private static String getHtmlRowResult(String key, String valve, 254 String trClass, String tdClassKey, String tdClassValue) { 255 return String.format(HTML_TR_TD + key + HTML_TD_END_TD + valve + HTML_TR_TD_END, 256 trClass, tdClassKey, tdClassValue); 257 } 258 getHtmlRowResult(String key, List<String> valve)259 private static String getHtmlRowResult(String key, List<String> valve) { 260 StringBuilder resValve = new StringBuilder(EMPTY_STRING); 261 for (String ele : valve) { 262 resValve.insert(0, HTML_LI_CIRCLE + ele + HTML_LI_END); 263 } 264 return HTML_TR_TD_DUPLICATE_KEY + key + HTML_TD_DUPLICATE_VALUE + resValve + HTML_UL_TD_TR_END; 265 } 266 getResultHtml(List<ParamModel> models)267 private static String getResultHtml(List<ParamModel> models) { 268 StringBuilder resultHtml = new StringBuilder(EMPTY_STRING); 269 resultHtml.append(HTML_UL_HEAD); 270 for (int i = 0; i < models.size(); i++) { 271 ParamModel model = models.get(i); 272 String md5Html = getHtmlRowResult(RESULT_MD5, model.getMd5(), 273 CLASS_DUPLICATE_LAYOUT, CLASS_DUPLICATE_KEY, CLASS_DUPLICATE_VALUE); 274 String sizeHtml = getHtmlRowResult(RESULT_SIZE, model.getSize(), 275 CLASS_DUPLICATE_LAYOUT, CLASS_DUPLICATE_KEY, CLASS_DUPLICATE_VALUE); 276 String filesHtml = getHtmlRowResult(RESULT_FILES, model.getFiles()); 277 String liHtml; 278 if (SHOW_SIZE > i) { 279 liHtml = HTML_LI_HEAD; 280 } else { 281 liHtml = HTML_LI_DUPLICATE; 282 } 283 String modelHtml = liHtml + HTML_TABLE_DUPLICATE 284 + md5Html + sizeHtml + filesHtml + HTML_TABLE_LI_END; 285 resultHtml.append(modelHtml); 286 } 287 resultHtml.append(HTML_UL_END); 288 if (models.size() > SHOW_SIZE) { 289 resultHtml.append(String.format(HTML_BUTTON_SHOW, DUPLICATE_FOLDER_NAME, DUPLICATE_FOLDER_NAME)); 290 resultHtml.append(String.format(HTML_BUTTON_HIDE, DUPLICATE_FOLDER_NAME, DUPLICATE_FOLDER_NAME)); 291 } 292 return resultHtml.toString(); 293 } 294 md5HashCode(String filePath)295 private String md5HashCode(String filePath) throws IOException, NoSuchAlgorithmException { 296 try (InputStream fis = new FileInputStream(filePath)) { 297 MessageDigest md = MessageDigest.getInstance(SHA_256); 298 byte[] buffer = new byte[MD5_BUFFER_SIZE]; 299 int length; 300 while ((length = fis.read(buffer, 0, MD5_BUFFER_SIZE)) != -1) { 301 md.update(buffer, 0, length); 302 } 303 byte[] md5Bytes = md.digest(); 304 BigInteger bigInt = new BigInteger(1, md5Bytes); 305 return bigInt.toString(MD5_LENGTH); 306 } catch (IOException e) { 307 LOG.error(ScanErrorEnum.STAT_DUPLICATE_INPUT_STREAM_ERROR + e.getMessage()); 308 throw new IOException(ScanErrorEnum.STAT_DUPLICATE_INPUT_STREAM_ERROR.msg + e.getMessage()); 309 } catch (NoSuchAlgorithmException e) { 310 LOG.error(ScanErrorEnum.STAT_DUPLICATE_MESSAGE_DIGEST_ERROR + e.getMessage()); 311 throw new NoSuchAlgorithmException(ScanErrorEnum.STAT_DUPLICATE_MESSAGE_DIGEST_ERROR.msg 312 + e.getMessage()); 313 } 314 } 315 splitPath(String path, String packageName)316 private String splitPath(String path, String packageName) { 317 String[] split = path.split(packageName); 318 if (split.length > 1) { 319 return split[1]; 320 } else { 321 return path; 322 } 323 } 324 } 325