pom
<!-- swagger依赖 -->
<dependency>
<groupId>com.spring4all</groupId>
<artifactId>swagger-spring-boot-starter</artifactId>
<version>1.8.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
CommnetUtil
package com.example.demo;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiParam;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.ClassUtils;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* java 注释工具
*/
public class CommnetUtil {
/**
* 反射时默认的包
*/
private static final String defaultPackage = "com.example.demo.";
private static final String get_prefix = " get";
private static final String get_field_name = "GET_FIELD_NAME";
private static final String set_prefix = " set";
private static final String set_field_name = "SET_FIELD_NAME";
private static final String field = "FIELD";
private static final String left_brackets = "(";
private static final String get_template = " /**\n" +
" * @return GET_FIELD_NAME\n" +
" */\n";
private static final String set_template = " /**\n" +
" * @param FIELD SET_FIELD_NAME\n" +
" */\n";
private static final String constructor_template = " /**\n" +
" * 构造函数\n" +
" */\n";
private static final List<String> annotationList = Collections.singletonList("@Override");
/**
* 处理 dto pojo vo 等类
*
*/
private static void buildDto(){
String path = "";
File file = new File("path");
if (!file.exists()) {
return;
}
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File item : files) {
processSingleFile(item);
}
return;
}
processSingleFile(file);
}
/**
* 处理单个类文件
* @param file 类文件
*/
private static void processSingleFile(File file) {
try {
Map<String, String> apiMap = buildApiMap(file);
File tempFile = File.createTempFile("code_temp_" + file.getName(), ".txt");
List<String> strings = FileUtils.readLines(file, "UTF-8");
LinkedList<String> linkedList = new LinkedList<>();
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
long preOffset = 0;
for (String string : strings) {
randomAccessFile.readLine();
long offset = randomAccessFile.getFilePointer();
doList(linkedList, string + "\n");
if (null == string || "".equals(string)) {
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
continue;
}
if (!(string.contains(left_brackets) && string.contains("public")) ) {
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
continue;
}
if (string.contains(set_prefix)) {
if (hadComment(linkedList)) {
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
continue;
}
String oriSetField = string.substring(string.indexOf(set_prefix) + set_prefix.length(), string.lastIndexOf(left_brackets));
String setField = oriSetField.toLowerCase();
System.out.println(setField);
boolean containsKey = apiMap.containsKey(setField);
if (!containsKey) {
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
continue;
}
String comment = set_template.replace(set_field_name, apiMap.get(setField)).replace(field,
oriSetField.substring(0, 1).toLowerCase() + oriSetField.substring(1));
RafUtil.append(tempFile, comment.getBytes(StandardCharsets.UTF_8));
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
continue;
}
if (string.contains(get_prefix)) {
if (hadComment(linkedList)) {
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
continue;
}
String getField = string.substring(string.indexOf(get_prefix) + get_prefix.length(), string.lastIndexOf(left_brackets)).toLowerCase();
boolean containsKey = apiMap.containsKey(getField);
if (!containsKey) {
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
continue;
}
String comment = get_template.replace(get_field_name, apiMap.get(getField));
RafUtil.append(tempFile, comment.getBytes(StandardCharsets.UTF_8));
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
continue;
}
if (string.contains(file.getName().replace(".java", ""))) {
if (hadComment(linkedList)) {
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
continue;
}
RafUtil.append(tempFile, constructor_template.getBytes(StandardCharsets.UTF_8));
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
}
}
FileUtils.copyFile(tempFile, file);
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
}
/**
* 存当前读取行的前3行内容
* @param list 存储容器
* @param line 当前行
*/
private static void doList(LinkedList<String> list, String line){
list.add(line);
int customSize = 3;
int size = list.size();
if (size >= customSize) {
list.removeFirst();
}
}
/**
* 构造一个 [方法名(小写)->注释内容] 的map
* @param file 操作的文件
* @return map
* @throws ClassNotFoundException ClassNotFoundException
* @throws IOException IOException
*/
private static Map<String,String> buildApiMap(File file) throws ClassNotFoundException, IOException {
String name = file.getName();
String className = name.substring(0, name.lastIndexOf("."));
Map<String, String> apiMap = new HashMap<>();
Class aClass = ClassUtils.getClass(defaultPackage + className);
for (Field declaredField : aClass.getDeclaredFields()) {
ApiModelProperty declaredAnnotation = declaredField.getDeclaredAnnotation(ApiModelProperty.class);
if (declaredAnnotation != null) {
apiMap.put(declaredField.getName().toLowerCase(), declaredAnnotation.value());
continue;
}
ApiParam apiParam = declaredField.getDeclaredAnnotation(ApiParam.class);
if (apiParam != null) {
apiMap.put(declaredField.getName().toLowerCase(), apiParam.value());
}
}
// 可能有些字段上没加 @ApiParam、@ApiModelProperty 等注解;就使用正则进行注释匹配,使用分组取出注释内容
List<String> lines = FileUtils.readLines(file, "UTF-8");
StringBuilder strBuilder = new StringBuilder();
for (String line : lines) {
strBuilder.append(line).append("\n");
}
String[] codeBlockArr = strBuilder.toString().split(";");
// javadoc注释:(前导空格 /** 换行)(前导空格 * 空格一个 )(注释内容)(前导空格 * 任意字符)(前导空格 private 后空格 类型声明)(字段名)
Pattern pattern = Pattern.compile("([\\s]*/[*]{2}\\n)([\\s]*[*][\\s])([\\w\u4e00-\u9fa5/+();()、,\\-: :]*)([\\s]*[*]{1}.*)([\\s]*private[\\s]*[\\w<>\\[\\]]*[\\s]*)([\\w\\d]*)");
// 双斜线注释: (前导空格 // 后空格)(注释内容)(换行)(前导空格 * 任意字符)(前导空格 private 后空格 类型声明)(字段名)
Pattern patternSlash = Pattern.compile("([\\s]*//[\\s]*)([\\w\u4e00-\u9fa5/+();()、,\\-: :]*)([\\n])([\\s]*private[\\s]*[\\w<>\\[\\]]*[\\s]*)([\\w\\d]*)");
for (String aPrivate: codeBlockArr) {
Matcher matcher = pattern.matcher(aPrivate);
if (matcher.find()) {
try {
String key = matcher.group(6);
String val = matcher.group(3);
if (!apiMap.containsKey(key.toLowerCase())) {
apiMap.put(key.toLowerCase(), val);
}
} catch (Exception e) {
e.printStackTrace();
}
continue;
}
Matcher matcherSlash = patternSlash.matcher(aPrivate);
if (matcherSlash.find()) {
try {
String key = matcherSlash.group(5);
String val = matcherSlash.group(2);
if (!apiMap.containsKey(key.toLowerCase())) {
apiMap.put(key.toLowerCase(), val);
}
} catch (Exception e) {
e.printStackTrace();
}
continue;
}
}
return apiMap;
}
/**
* 判断当前行的前几行有没有javadoc注释(有的话就不加注释了,防止重复)
* @param linkedList 存放前几行的容器
* @return 有-true; 没有-fals
*/
private static boolean hadComment(LinkedList<String> linkedList) {
StringBuilder strBuilder = new StringBuilder();
for (String line : linkedList) {
strBuilder.append(line);
}
String pre3Line = strBuilder.toString();
Pattern pattern = Pattern.compile("([\\s]*[*]{1}\\.*)");
return pattern.matcher(pre3Line).find();
}
/**
* 去除层后缀获取文件名
* @param file 文件
* @return 截取后的文件名,没有匹配到后缀时,返回原文件名
*/
private static String getName(File file){
String mapper_suffix = "Mapper";
String service_suffix = "Service";
String serviceImpl_suffix = "ServiceImpl";
String controller_suffix = "Controller";
String fileName = file.getName();
String name = fileName;
if (fileName.contains(mapper_suffix)) {
name = fileName.substring(0, fileName.lastIndexOf(mapper_suffix));
} else if (fileName.contains(service_suffix)) {
name = fileName.substring(0, fileName.lastIndexOf(service_suffix));
} else if (fileName.contains(serviceImpl_suffix)) {
name = fileName.substring(0, fileName.lastIndexOf(serviceImpl_suffix));
} else if (fileName.contains(controller_suffix)) {
name = fileName.substring(0, fileName.lastIndexOf(controller_suffix));
} else {
System.out.println("no_suffix: " + fileName);
}
return name;
}
/**
* 构造一个[文件名,[方法名,方法整个注释内容]] 的map
* @param file 要处理的当前文件
* @param fileNameMap 结果map
*/
private static void buildMethodMap(File file, HashMap<String, HashMap<String, String>> fileNameMap) {
String name = getName(file);
if (null == name) {
return ;
}
HashMap<String, String> methodName_CommentMap = new HashMap<>();
fileNameMap.put(name, methodName_CommentMap);
try {
List<String> lines = FileUtils.readLines(file, "UTF-8");
StringBuilder strBuilder = new StringBuilder();
for (String line : lines) {
strBuilder.append(line).append("\n");
}
String[] codeBlockArr = strBuilder.toString().split(";");
for (String codeBlock : codeBlockArr) {
if (codeBlock.contains("{")) {
for (String lineBlock : codeBlock.split("\\{")) {
processCodeBlock(lineBlock, methodName_CommentMap);
}
continue;
}
processCodeBlock(codeBlock, methodName_CommentMap);
}
} catch (IOException e) {
e.printStackTrace();
}
}
// tab 开头的 javadoc 注释
static Pattern pattern_t = Pattern.compile("([\\t].*/[*]{2}.*\\n[\\s\\S]*[*]{1}[/]{1}\\n)([\\s]*[a-zA-Z<>\\[\\] ]*[\\s]{1,3})(\\b\\w*[(])");
// 空格开头的 javadoc 注释
static Pattern pattern3 = Pattern.compile("([\\s]{4}/[*]{2}.*\\n[\\s\\S]*[*]{1}[/]{1}\\n)([\\s]*[a-zA-Z<>\\[\\] ]*[\\s]{1,3})(\\b\\w*[(])");
// 空格开头的 javadoc 注释;方法上方有注解
static Pattern pattern4 = Pattern.compile("([\\s]{4}/[*]{2}.*\\n[\\s\\S]*[*]{1}[/]{1}\\n)([\\s]*[@].*\\n)([\\s]*[a-zA-Z<>\\[\\] ]*[\\s]{1,3})(\\b\\w*[(])");
private static void processCodeBlock(String codeBlock, HashMap<String, String> methodName_CommentMap){
Matcher matcher = pattern3.matcher(codeBlock);
if (matcher.find()) {
try {
String key = matcher.group(3);
String val = matcher.group(1);
key = processKey(key);
methodName_CommentMap.put(key.substring(0, key.indexOf("(")), val);
} catch (Exception e) {
e.printStackTrace();
}
return;
}
Matcher matcher4 = pattern4.matcher(codeBlock);
if (matcher4.find()) {
try {
String key = matcher4.group(4);
String val = matcher4.group(1);
key = processKey(key);
methodName_CommentMap.put(key.substring(0, key.indexOf("(")), val);
} catch (Exception e) {
e.printStackTrace();
}
return;
}
Matcher matcherT = pattern_t.matcher(codeBlock);
if (matcherT.find()) {
try {
String key = matcherT.group(3);
String val = matcherT.group(1);
key = processKey(key);
methodName_CommentMap.put(key.substring(0, key.indexOf("(")), val);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 去除key前面的多余内容
* @param key 要处理的key
* @return key
*/
private static String processKey(String key) {
if (key == null) {
return null;
}
String trim = key.trim();
return trim.substring(key.indexOf(" ") + 1);
}
private static void syncCommnet() {
String src = "";
String dest = "";
File file = new File(src);
File fileDest = new File(dest);
if (!file.exists() || !fileDest.exists()) {
return;
}
HashMap<String, HashMap<String, String>> fileNameMap = new HashMap<>();
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File item : files) {
if (item.isDirectory()) {
System.out.println(item.getName() + ": is_dir");
continue;
}
buildMethodMap(item, fileNameMap);
}
} else {
buildMethodMap(file, fileNameMap);
}
// src dir ok
//
boolean directory = fileDest.isDirectory();
if (directory) {
File[] files = fileDest.listFiles();
for (File item : files) {
syncFile(item, fileNameMap.get(getName(item)));
System.out.println(item.getName() + "==>> OK");
}
} else {
syncFile(fileDest, fileNameMap.get(getName(fileDest)));
}
}
private static void syncFile(File file, Map<String, String> methodNameMap) {
if (methodNameMap == null) {
return;
}
try {
File tempFile = File.createTempFile("code_temp_" + file.getName(), ".txt");
List<String> strings = FileUtils.readLines(file, "UTF-8");
LinkedList<String> linkedList = new LinkedList<>();
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
long preOffset = 0;
String preLine = null;
for (String string : strings) {
randomAccessFile.readLine();
long offset = randomAccessFile.getFilePointer();
doList(linkedList, string + "\n");
if (null == string || "".equals(string)) {
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
continue;
}
if (annotationList.contains(string.trim())) {
preLine = string + "\n";
preOffset = offset;
continue;
}
if (!(string.contains(left_brackets))) {
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
continue;
}
if (hadComment(linkedList)) {
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
continue;
}
String[] blockArr = string.split(" ");
for (String block : blockArr) {
if (block == null) {
continue;
}
if (methodNameMap.containsKey(block.trim())) {
RafUtil.append(tempFile, (methodNameMap.get(block.trim())).getBytes(StandardCharsets.UTF_8));
if (preLine != null) {
RafUtil.append(tempFile, preLine.getBytes(StandardCharsets.UTF_8));
preLine = null;
}
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
break;
}
// 括号和方法名在一起的情况
if (block.contains("(")) {
String methodName = block.substring(0, block.indexOf("("));
if (methodNameMap.containsKey(methodName.trim())) {
RafUtil.append(tempFile, (methodNameMap.get(methodName.trim())).getBytes(StandardCharsets.UTF_8));
if (preLine != null) {
RafUtil.append(tempFile, preLine.getBytes(StandardCharsets.UTF_8));
preLine = null;
}
RafUtil.append(tempFile, RafUtil.getBytes(file, preOffset, offset));
preOffset = offset;
break;
}
}
}
}
FileUtils.copyFile(tempFile, file);
} catch (IOException e) {
e.printStackTrace();
}
}
}