/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ohos;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
/**
* scanDuplicate info
*
* @since 2023/11/27
*/
public class ScanStatDuplicate {
private static final String UNPACK_NAME = "unpack";
private static final String DUPLICATE_FOLDER_NAME = "duplicate";
private static final String EMPTY_STRING = "";
private static final String SHA_256 = "SHA-256";
private static final String RESULT_MD5 = "md5";
private static final String RESULT_SIZE = "size";
private static final String RESULT_FILES = "files";
private static final String DUPLICATE_DESC = "find the duplicated files";
private static final String DUPLICATE_PARAM = "--stat-duplicate";
private static final String TASK_TYPE = "taskType";
private static final String TASK_DESC = "taskDesc";
private static final String PARAM = "param";
private static final String START_TIME = "startTime";
private static final String STOP_TIME = "stopTime";
private static final String RESULT = "result";
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss:SSS";
private static final int MD5_BUFFER_SIZE = 1024;
private static final long DUPLICATE_TYPE = 1L;
private static final int MD5_LENGTH = 16;
private static final long SHOW_SIZE = 10L;
private static final String HTML_BUTTON_SHOW = "";
private static final String HTML_BUTTON_HIDE = "";
private static final String HTML_TABLE_BOX = "
";
private static final String HTML_TABLE_END = "
";
private static final String HTML_TR_TD_LAYOUT = "
";
private static final String HTML_TR_TD_VALUE = "
";
private static final String HTML_TR_TD_END = "
";
private static final String HTML_TR_TD_RESULT = "
";
private static final String HTML_TR_TD = "
";
private static final String HTML_TD_END_TD = "
";
private static final String HTML_LI_CIRCLE = "
";
private static final String HTML_LI_END = "
";
private static final String HTML_TR_TD_DUPLICATE_KEY = "
";
private static final String HTML_TD_DUPLICATE_VALUE = "
";
private static final String HTML_UL_TD_TR_END = "
";
private static final String HTML_UL_HEAD = "
";
private static final String CLASS_DUPLICATE_LAYOUT = " class=\"duplicateLayout\"";
private static final String CLASS_DUPLICATE_KEY = " class=\"duplicateKey\"";
private static final String CLASS_DUPLICATE_VALUE = " class=\"duplicateValue\"";
private static final String HTML_LI_HEAD = "
";
private static final String HTML_LI_DUPLICATE = "
";
private static final String HTML_TABLE_DUPLICATE = "
";
private static final String HTML_TABLE_LI_END = "
";
private static final String HTML_UL_END = "
";
private static final Log LOG = new Log(ScanStatDuplicate.class.toString());
private static class ParamModel {
private String md5 = EMPTY_STRING;
private long size;
private List files = new ArrayList<>();
public String getMd5() {
return this.md5;
}
public void setMd5(String md5) {
this.md5 = md5;
}
public long getSize() {
return this.size;
}
public void setSize(long size) {
this.size = size;
}
public List getFiles() {
return this.files;
}
public void setFiles(List files) {
this.files = files;
}
}
private static class DuplicateResult {
private long taskType = DUPLICATE_TYPE;
private String taskDesc = DUPLICATE_DESC;
private String param = DUPLICATE_PARAM;
private String startTime = EMPTY_STRING;
private String stopTime = EMPTY_STRING;
private List result = new ArrayList<>();
public long getTaskType() {
return this.taskType;
}
public void setTaskType(long taskType) {
this.taskType = taskType;
}
public String getTaskDesc() {
return this.taskDesc;
}
public void setTaskDesc(String taskDesc) {
this.taskDesc = taskDesc;
}
public String getParam() {
return this.param;
}
public void setParam(String param) {
this.param = param;
}
public String getStartTime() {
return this.startTime;
}
public void setStartTime(String startTime) {
this.startTime = startTime;
}
public String getStopTime() {
return this.stopTime;
}
public void setStopTime(String stopTime) {
this.stopTime = stopTime;
}
public List getResult() {
return this.result;
}
public void setResult(List result) {
this.result = result;
}
}
/**
* scan statDuplicate.
*
* @param utility utility data
* @param jsonList List data
* @param fileList List data
* @return duplicate html
* @throws IOException Throws this exception if getDuplicateResList exception
* @throws NoSuchAlgorithmException Throws this exception if getDuplicateResList exception
*/
public String statDuplicate(Utility utility, List jsonList, List fileList)
throws IOException, NoSuchAlgorithmException {
DuplicateResult duplicateResult = new DuplicateResult();
duplicateResult.setStartTime(getCurrentTime());
List resList = getDuplicateResList(fileList);
File parentFile = new File(utility.getOutPath());
if (!parentFile.exists() && !parentFile.mkdirs()) {
LOG.error(ScanErrorEnum.STAT_DUPLICATE_CREATE_FILE_ERROR.toString());
}
List filterList = new ArrayList<>();
for (ParamModel model : resList) {
List files = model.getFiles();
if (files.size() > 1) {
filterList.add(model);
}
}
duplicateResult.setResult(filterList);
duplicateResult.setStopTime(getCurrentTime());
String taskTypeHtml = getHtmlRow(TASK_TYPE, duplicateResult.getTaskType());
String taskDescHtml = getHtmlRow(TASK_DESC, duplicateResult.getTaskDesc());
String paramHtml = getHtmlRow(PARAM, duplicateResult.getParam());
String startTimeHtml = getHtmlRow(START_TIME, duplicateResult.getStartTime());
String stopTimeHtml = getHtmlRow(STOP_TIME, duplicateResult.getStopTime());
String resultValue = getResultHtml(duplicateResult.getResult());
String resultHtml = getHtmlRowResultClass(RESULT, resultValue);
String htmlStr = HTML_TABLE_BOX + taskTypeHtml + taskDescHtml + paramHtml
+ startTimeHtml + stopTimeHtml + resultHtml + HTML_TABLE_END;
String jsonStr = JSON.toJSONString(duplicateResult, new SerializerFeature[] {
SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteDateUseDateFormat});
jsonList.add(jsonStr);
return htmlStr;
}
private static String getCurrentTime() {
long currentTimeMillis = System.currentTimeMillis();
return new SimpleDateFormat(DATE_FORMAT).format(currentTimeMillis);
}
private List getDuplicateResList(List fileList) throws IOException, NoSuchAlgorithmException {
List resList = new ArrayList<>();
for (String filePath : fileList) {
boolean addFlag = true;
String md5 = md5HashCode(filePath);
for (ParamModel element : resList) {
String eleMd5 = element.getMd5();
if (eleMd5.equals(md5)) {
List eleFiles = element.getFiles();
eleFiles.add(splitPath(filePath, UNPACK_NAME));
element.setFiles(eleFiles);
addFlag = false;
}
}
if (addFlag) {
ParamModel model = new ParamModel();
long size = FileUtils.getFileSize(filePath);
model.setMd5(md5);
model.setSize(size);
List files = model.getFiles();
files.add(splitPath(filePath, UNPACK_NAME));
resList.add(model);
}
}
return resList;
}
private static String getHtmlRow(String key, long valve) {
return HTML_TR_TD_LAYOUT + key + HTML_TR_TD_VALUE + valve + HTML_TR_TD_END;
}
private static String getHtmlRow(String key, String valve) {
return HTML_TR_TD_LAYOUT + key + HTML_TR_TD_VALUE + valve + HTML_TR_TD_END;
}
private static String getHtmlRowResultClass(String key, String valve) {
return HTML_TR_TD_RESULT + key + HTML_TR_TD_VALUE + valve + HTML_TR_TD_END;
}
private static String getHtmlRowResult(String key, long valve,
String trClass, String tdClassKey, String tdClassValue) {
return String.format(HTML_TR_TD + key + HTML_TD_END_TD + valve + HTML_TR_TD_END,
trClass, tdClassKey, tdClassValue);
}
private static String getHtmlRowResult(String key, String valve,
String trClass, String tdClassKey, String tdClassValue) {
return String.format(HTML_TR_TD + key + HTML_TD_END_TD + valve + HTML_TR_TD_END,
trClass, tdClassKey, tdClassValue);
}
private static String getHtmlRowResult(String key, List valve) {
StringBuilder resValve = new StringBuilder(EMPTY_STRING);
for (String ele : valve) {
resValve.insert(0, HTML_LI_CIRCLE + ele + HTML_LI_END);
}
return HTML_TR_TD_DUPLICATE_KEY + key + HTML_TD_DUPLICATE_VALUE + resValve + HTML_UL_TD_TR_END;
}
private static String getResultHtml(List models) {
StringBuilder resultHtml = new StringBuilder(EMPTY_STRING);
resultHtml.append(HTML_UL_HEAD);
for (int i = 0; i < models.size(); i++) {
ParamModel model = models.get(i);
String md5Html = getHtmlRowResult(RESULT_MD5, model.getMd5(),
CLASS_DUPLICATE_LAYOUT, CLASS_DUPLICATE_KEY, CLASS_DUPLICATE_VALUE);
String sizeHtml = getHtmlRowResult(RESULT_SIZE, model.getSize(),
CLASS_DUPLICATE_LAYOUT, CLASS_DUPLICATE_KEY, CLASS_DUPLICATE_VALUE);
String filesHtml = getHtmlRowResult(RESULT_FILES, model.getFiles());
String liHtml;
if (SHOW_SIZE > i) {
liHtml = HTML_LI_HEAD;
} else {
liHtml = HTML_LI_DUPLICATE;
}
String modelHtml = liHtml + HTML_TABLE_DUPLICATE
+ md5Html + sizeHtml + filesHtml + HTML_TABLE_LI_END;
resultHtml.append(modelHtml);
}
resultHtml.append(HTML_UL_END);
if (models.size() > SHOW_SIZE) {
resultHtml.append(String.format(HTML_BUTTON_SHOW, DUPLICATE_FOLDER_NAME, DUPLICATE_FOLDER_NAME));
resultHtml.append(String.format(HTML_BUTTON_HIDE, DUPLICATE_FOLDER_NAME, DUPLICATE_FOLDER_NAME));
}
return resultHtml.toString();
}
private String md5HashCode(String filePath) throws IOException, NoSuchAlgorithmException {
try (InputStream fis = new FileInputStream(filePath)) {
MessageDigest md = MessageDigest.getInstance(SHA_256);
byte[] buffer = new byte[MD5_BUFFER_SIZE];
int length;
while ((length = fis.read(buffer, 0, MD5_BUFFER_SIZE)) != -1) {
md.update(buffer, 0, length);
}
byte[] md5Bytes = md.digest();
BigInteger bigInt = new BigInteger(1, md5Bytes);
return bigInt.toString(MD5_LENGTH);
} catch (IOException e) {
LOG.error(ScanErrorEnum.STAT_DUPLICATE_INPUT_STREAM_ERROR + e.getMessage());
throw new IOException(ScanErrorEnum.STAT_DUPLICATE_INPUT_STREAM_ERROR.msg + e.getMessage());
} catch (NoSuchAlgorithmException e) {
LOG.error(ScanErrorEnum.STAT_DUPLICATE_MESSAGE_DIGEST_ERROR + e.getMessage());
throw new NoSuchAlgorithmException(ScanErrorEnum.STAT_DUPLICATE_MESSAGE_DIGEST_ERROR.msg
+ e.getMessage());
}
}
private String splitPath(String path, String packageName) {
String[] split = path.split(packageName);
if (split.length > 1) {
return split[1];
} else {
return path;
}
}
}