/* * Copyright (C) 2018 niaoge<[email protected]> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.stategen.framework.generator.util; import java.io.CharArrayReader; import java.io.CharArrayWriter; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; import org.stategen.framework.lite.CaseInsensitiveHashMap; import org.stategen.framework.util.CollectionUtil; import org.stategen.framework.util.StringUtil; import cn.org.rapid_framework.generator.util.GLogger; import com.github.javaparser.JavaParser; import com.github.javaparser.ParseException; import com.github.javaparser.Position; import com.github.javaparser.Range; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.ImportDeclaration; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.BodyDeclaration; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.FieldDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.VariableDeclarator; import com.github.javaparser.ast.comments.Comment; import com.github.javaparser.ast.comments.JavadocComment; import com.github.javaparser.ast.expr.AnnotationExpr; import com.github.javaparser.ast.expr.Name; import com.github.javaparser.ast.type.ClassOrInterfaceType; import com.github.javaparser.printer.PrettyPrinterConfiguration; /** * The Class ASTHelper. */ class ASTHelper { /** * Gets the one nodes by type. * * @param <N> the number type * @param container the container * @param clazz the clazz * @return the one nodes by type */ @SuppressWarnings("unchecked") static <N extends Node> N getOneNodesByType(Node container, Class<N> clazz) { for (Node child : container.getChildNodes()) { if (clazz.isInstance(child)) { return (N) child; } Node result = getOneNodesByType(child, clazz); if (result != null) return (N) result; } return null; } /** * Gets the type index. * * @param <N> the number type * @param container the container * @param clazz the clazz * @return the type index */ static <N extends Node> Integer getTypeIndex(Node container, Class<N> clazz) { List<Node> children = container.getChildNodes(); if (CollectionUtil.isNotEmpty(children)) { for (int i = 0; i < children.size(); i++) { Node child = children.get(i); if (clazz.isInstance(child)) { return i; } Integer temIdx = getTypeIndex(child, clazz); if (temIdx != null) return temIdx; } } return null; } /** * Gets the nodes by type. * * @param <N> the number type * @param container the container * @param clazz the clazz * @return the nodes by type */ static <N extends Node> List<N> getNodesByType(Node container, Class<N> clazz) { List<N> nodes = new ArrayList<N>(); boolean find = false; for (Node child : container.getChildNodes()) { if (clazz.isInstance(child)) { nodes.add(clazz.cast(child)); find = true; } if (!find) { nodes.addAll(getNodesByType(child, clazz)); } } return nodes; } /** * Prints the self and children. * * @param container the container */ static void printSelfAndChildren(Node container) { GLogger.println("当前类为:" + container.getClass() + "..................."); for (Node child : container.getChildNodes()) { GLogger.info("第一级子类为:" + child.getClass() + "..................." + child); } GLogger.println("打印结束"); GLogger.println("\n\n\n"); } /** * Gets the field declaration map. * * @param <T> the generic type * @param node the class declaration * @param type the type * @return the field declaration map */ static <T extends BodyDeclaration> Map<String, T> getBodyDeclarationMap(Node node, Class<T> type) { List<Node> children = node.getChildNodes(); if (CollectionUtil.isEmpty(children)) { return new HashMap<String, T>(0); } Map<String, T> bodyMap = new CaseInsensitiveHashMap<T>(children.size()); List<T> bodyDeclarations = getNodesByType(node, type); for (T bodyDeclaration : bodyDeclarations) { if (bodyDeclaration instanceof FieldDeclaration) { FieldDeclaration fieldDeclaration = (FieldDeclaration) bodyDeclaration; VariableDeclarator variableDeclaratorId = getOneNodesByType(fieldDeclaration, VariableDeclarator.class); bodyMap.put(variableDeclaratorId.getName().toString(), bodyDeclaration); } else if (bodyDeclaration instanceof MethodDeclaration) { MethodDeclaration methodDeclaration = (MethodDeclaration) bodyDeclaration; bodyMap.put(methodDeclaration.getNameAsString(), bodyDeclaration); } } return bodyMap; } /** * Gets the field map. * * @param node the node * @return the field map */ static Map<String, String> getFieldMap(Node node) { Map<String, String> fieldmap = null; Map<String, FieldDeclaration> fieldDeclarationMap = getBodyDeclarationMap(node, FieldDeclaration.class); if (fieldDeclarationMap != null) { fieldmap = new CaseInsensitiveHashMap<String>(fieldDeclarationMap.size()); for (String oldFieldName : fieldDeclarationMap.keySet()) { fieldmap.put(oldFieldName, oldFieldName); } } return fieldmap; } private static boolean checkAnnoInComments(String comments, String annoName) { if (StringUtil.isNotEmpty(comments)) { Pattern commentPt = Pattern.compile("//\\s*@"+annoName, Pattern.DOTALL); if (commentPt.matcher(comments).find()){ return true; } Pattern notPit = Pattern.compile("[email protected]"+annoName, Pattern.DOTALL); if (notPit.matcher(comments).find()){ return true; } } return false; } /** * Adds the annotaion declare. * * @param oldAnnotationExprs the last annotation exprs * @param newDeclaration the body declaration * @return true, if successful * @throws InstantiationException the instantiation exception * @throws IllegalAccessException the illegal access exception */ static boolean addAnnotationExprsToBody(BodyDeclaration oldDeclaration, BodyDeclaration newDeclaration, boolean replace) throws InstantiationException, IllegalAccessException { boolean modified = false; Set<String> disabledAnnoNames = new LinkedHashSet<String>(); String oldCommentText = getAllCommentsText(oldDeclaration); NodeList<AnnotationExpr> newAnnotations = newDeclaration.getAnnotations(); if (newAnnotations != null) { for (int i = newAnnotations.size() - 1; i >= 0; i--) { AnnotationExpr annotationExpr = newAnnotations.get(i); String annoName = annotationExpr.getNameAsString(); boolean checkAnnoInComments = checkAnnoInComments(oldCommentText, annoName); if (checkAnnoInComments) { newAnnotations.remove(i); disabledAnnoNames.add(annoName); modified = true; } } } NodeList<AnnotationExpr> oldAnnotationExprs = oldDeclaration.getAnnotations(); if (CollectionUtil.isNotEmpty(oldAnnotationExprs)) { //TODO List怎么处理? Map<String, AnnotationExpr> tempMap = CollectionUtil.toMap(newAnnotations, AnnotationExpr::getNameAsString); for (int i = oldAnnotationExprs.size() - 1; i >= 0; i--) { AnnotationExpr oldAnnotationExpr = oldAnnotationExprs.get(i); String annoName = oldAnnotationExpr.getNameAsString(); if (checkAnnoInComments(oldCommentText, annoName)) { oldAnnotationExprs.remove(i); disabledAnnoNames.add(annoName); continue; } if (newAnnotations == null) { continue; } String oldAnnoName = annoName; if (!tempMap.containsKey(oldAnnoName)) { newAnnotations.add(oldAnnotationExpr); } else { //使用旧的标注 if (replace) { if (newDeclaration instanceof FieldDeclaration) { FieldDeclaration fieldDeclaration = (FieldDeclaration) newDeclaration; String elementType = fieldDeclaration.getElementType().toString(); if (!elementType.equals("String") || !elementType.equals("java.lang.String")) { if (oldAnnoName.equals("Max") || oldAnnoName.equals("Min")) { continue; } } //把之前没有title的,如果数据库更换后,换成数据库的title,之后就不能自动换了 if (oldAnnoName.equals("ApiModelProperty")) { VariableDeclarator variableDeclaratorId = getOneNodesByType(fieldDeclaration, VariableDeclarator.class); String fieldName = variableDeclaratorId.getName().asString(); List<Node> oldChildNodes = oldAnnotationExpr.getChildNodes(); boolean findValueAsFieldName = false; if (CollectionUtil.isNotEmpty(oldChildNodes)) { for (Node oldNode : oldChildNodes) { String oldNodeValue = oldNode.toString(); if (oldNodeValue.startsWith("\"") && oldNodeValue.endsWith("\"")) { oldNodeValue = oldNodeValue.substring(1, oldNodeValue.length() - 1).replaceAll(" ", ""); } if (oldNodeValue.equals(fieldName)) { findValueAsFieldName = true; break; } } } if (findValueAsFieldName) { continue; } } } AnnotationExpr annotationExpr = tempMap.get(oldAnnoName); if (annotationExpr != null) { //TODO,如果备注中不需要那个标准,就不加进来,同时移除现有的标注 int indexOf = newAnnotations.indexOf(annotationExpr); newAnnotations.remove(indexOf); tempMap.remove(oldAnnoName); newAnnotations.add(indexOf, oldAnnotationExpr); modified = true; } } } } if (newAnnotations == null && oldAnnotationExprs.size() > 0) { newDeclaration.setAnnotations(oldAnnotationExprs); modified = true; } } if (CollectionUtil.isNotEmpty(disabledAnnoNames)) { Optional<Comment> comment = newDeclaration.getComment(); String oldBlockCommentText = ""; if (comment.isPresent()) { oldBlockCommentText = comment.get().getContent(); for (String annoName : disabledAnnoNames) { if (checkAnnoInComments(oldBlockCommentText, annoName)) { disabledAnnoNames.remove(annoName); } } } if (CollectionUtil.isNotEmpty(disabledAnnoNames)) { StringBuffer sb = new StringBuffer(oldBlockCommentText); for (String annoName : disabledAnnoNames) { sb.append(" ").append("[email protected]").append(annoName); } newDeclaration.setComment(new JavadocComment(sb.toString())); modified = true; } } return modified; } private static String getAllCommentsText(BodyDeclaration declaration) { List<Comment> allContainedComments = declaration.getAllContainedComments(); StringBuffer sb = new StringBuffer(); boolean append =false; Optional<Comment> commentOp = declaration.getComment(); if (commentOp.isPresent()){ sb.append(commentOp.get().getContent()) ; append=true; } for (Comment comment : allContainedComments) { if (append){ sb.append(" "); } sb.append(comment.toString()); append=true; } String oldCommentText = sb.toString(); return oldCommentText; } /** * Gets the imports. * * @param cu the cu * @return the imports */ static Set<String> getImports(CompilationUnit cu) { Set<String> importSet = null; List<ImportDeclaration> imports = cu.getImports(); if (CollectionUtil.isNotEmpty(imports)) { importSet = new HashSet<String>(imports.size()); for (ImportDeclaration im : imports) { String imStr = (im.isStatic() ? "static " : "") + im.getName() + (im.isAsterisk() ? ".*" : ""); importSet.add(imStr); } } else { importSet = new HashSet<String>(0); } return importSet; } /** * The Class ImportComparator. */ static class ImportComparator implements java.util.Comparator<ImportDeclaration> { static final PrettyPrinterConfiguration prettyPrinterConfiguration; static { prettyPrinterConfiguration = new PrettyPrinterConfiguration(); prettyPrinterConfiguration.setPrintComments(false); } @Override public int compare(ImportDeclaration o1, ImportDeclaration o2) { String stringWithoutComments1 = o1.getName().toString(prettyPrinterConfiguration); String stringWithoutComments2 = o2.getName().toString(prettyPrinterConfiguration); if (o1.isStatic() && !o2.isStatic()) { return -1; } else if (!o1.isStatic() && o2.isStatic()) { return 1; } else if (o1.isStatic() && o2.isStatic()) { return stringWithoutComments1.compareTo(stringWithoutComments2); } int compareEclipseImport = compareEclipseImport(stringWithoutComments1, stringWithoutComments2, "java."); if (compareEclipseImport != -2) { return compareEclipseImport; } compareEclipseImport = compareEclipseImport(stringWithoutComments1, stringWithoutComments2, "javax."); if (compareEclipseImport != -2) { return compareEclipseImport; } compareEclipseImport = compareEclipseImport(stringWithoutComments1, stringWithoutComments2, "org."); if (compareEclipseImport != -2) { return compareEclipseImport; } compareEclipseImport = compareEclipseImport(stringWithoutComments1, stringWithoutComments2, "cn."); if (compareEclipseImport != -2) { return compareEclipseImport; } compareEclipseImport = compareEclipseImport(stringWithoutComments1, stringWithoutComments2, "com."); if (compareEclipseImport != -2) { return compareEclipseImport; } return stringWithoutComments1.compareTo(stringWithoutComments2); } private int compareEclipseImport(String stringWithoutComments1, String stringWithoutComments2, String importStartsWith) { if (stringWithoutComments1.startsWith(importStartsWith) && !stringWithoutComments2.startsWith(importStartsWith)) { return -1; } else if (!stringWithoutComments1.startsWith(importStartsWith) && stringWithoutComments2.startsWith(importStartsWith)) { return 1; } else if (stringWithoutComments1.startsWith(importStartsWith) && stringWithoutComments2.startsWith(importStartsWith)) { return stringWithoutComments1.compareTo(stringWithoutComments2); } return -2; } } protected static void addBlankImports(List<ImportDeclaration> imports, CompilationUnit nowCompilationUnit) { String lastStartWith = null; int size = imports.size(); for (int i = size - 1; i >= 0; i--) { ImportDeclaration importDeclaration = imports.get(i); String importName = importDeclaration.getName().toString(); int idx = importName.indexOf('.'); if (idx > 0) { String nowStrartWith = importName.substring(0, idx + 1); if (lastStartWith != null && !lastStartWith.equals(nowStrartWith)) { Range range = new Range(Position.pos(0, 0), Position.pos(0, 0)); ImportDeclaration emptyDeclaration = new ImportDeclaration(range, new Name(), false, false); imports.add(i + 1, emptyDeclaration); lastStartWith = null; } else { lastStartWith = nowStrartWith; } } } } /** * 将旧的import插入到新import中,还有Field上的标注. * * @param lastCompilationUnit the last compilation unit * @param isEntity the is entity * @param nowJavaUnitCharArrayWriter the now java unit char array writer * @throws IOException Signals that an I/O exception has occurred. * @throws ParseException the parse exception * @throws InstantiationException the instantiation exception * @throws IllegalAccessException the illegal access exception */ static void replaceJava(CompilationUnit lastCompilationUnit, JavaType javaType, CharArrayWriter nowJavaUnitCharArrayWriter, String shortFileName) throws IOException, ParseException, InstantiationException, IllegalAccessException { boolean replaced = false; CompilationUnit nowCompilationUnit = null; Reader nowFileReader = new CharArrayReader(nowJavaUnitCharArrayWriter.toCharArray()); try { //下面这种不会产生乱码 nowCompilationUnit = JavaParser.parse(nowFileReader); } catch (Exception e) { String erroJavaText = nowJavaUnitCharArrayWriter.toString(); erroJavaText = IOHelpers.addLineNumber(erroJavaText); GLogger.error("错误的java文件:\n" + erroJavaText); throw e; } try { switch (javaType) { case isControllerBase: case isController: case isService: case isServiceInternal: case isServiceImpl: { CompilationUnit temp = lastCompilationUnit; lastCompilationUnit = nowCompilationUnit; nowCompilationUnit = temp; replaced = true; break; } default: break; } Set<String> lastImportSet = getImports(lastCompilationUnit); Set<String> nowImportSet = getImports(nowCompilationUnit); //将之前的java引用加上去 List<ImportDeclaration> imports = nowCompilationUnit.getImports(); for (String lastImpt : lastImportSet) { if (!nowImportSet.contains(lastImpt)) { lastImpt = lastImpt.trim(); ImportDeclaration newImportDeclaration = new ImportDeclaration(new Name(lastImpt), false, false); imports.add(newImportDeclaration); GLogger.info("引用:" + lastImpt + " 被保留或增加"); replaced = true; } } Collections.sort(imports, new ImportComparator()); addBlankImports(imports, nowCompilationUnit); if (JavaType.isDao == javaType || JavaType.isDaoImpl == javaType) { return; } ClassOrInterfaceDeclaration oldClassDeclaration = getOneNodesByType(lastCompilationUnit, ClassOrInterfaceDeclaration.class); if (null != oldClassDeclaration) { ClassOrInterfaceDeclaration nowClassDeclaration = getOneNodesByType(nowCompilationUnit, ClassOrInterfaceDeclaration.class); if (null != nowClassDeclaration) { //将之前java类 标注加入 ,不再处理controller ,service,serviceImpl if (JavaType.isEntry == javaType) { if (addAnnotationExprsToBody(oldClassDeclaration, nowClassDeclaration, true)) { replaced = true; } } Map<String, FieldDeclaration> oldFieldDeclarationMap = getBodyDeclarationMap(oldClassDeclaration, FieldDeclaration.class); Map<String, FieldDeclaration> nowFieldDeclarationMap = getBodyDeclarationMap(nowClassDeclaration, FieldDeclaration.class); //把之前的Field加进来 int fieldIdx = -1; for (Entry<String, FieldDeclaration> oldEntry : oldFieldDeclarationMap.entrySet()) { String fieldName = oldEntry.getKey(); if (!nowFieldDeclarationMap.containsKey(fieldName)) { FieldDeclaration oldFieldDeclaration = oldEntry.getValue(); //methodIndex = methodIndex - 2; fieldIdx++; GLogger.info(fieldIdx + " oldFieldDeclaration<===========>:" + oldFieldDeclaration); nowClassDeclaration.getMembers().add(fieldIdx, oldFieldDeclaration); replaced = true; GLogger.println("属性(field)被保留或增加:" + fieldName + " 被保留<<<<<<<<<<<<<<<<<<<<<<"); } } //把Field上之前的标注注加进来,不再处理controller ,service,serviceImpl if (JavaType.isEntry == javaType) { for (Entry<String, FieldDeclaration> nowEntry : nowFieldDeclarationMap.entrySet()) { FieldDeclaration nowFieldDeclaration = nowEntry.getValue(); String fieldName = nowEntry.getKey(); FieldDeclaration oldFieldDeclaration = oldFieldDeclarationMap.get(fieldName); if (oldFieldDeclaration == null) { continue; } if (addAnnotationExprsToBody(oldFieldDeclaration, nowFieldDeclaration, true)) { replaced = true; } //把Modifile加进来 if (oldFieldDeclaration.getModifiers() != null) { nowFieldDeclaration.getModifiers().addAll(oldFieldDeclaration.getModifiers()); } //oldFieldDeclarationMap.remove(fieldName); } } //把之前的方法加进来 Map<String, MethodDeclaration> oldMethodDeclarationMap = getBodyDeclarationMap(oldClassDeclaration, MethodDeclaration.class); Map<String, MethodDeclaration> nowMethodDeclarationMap = getBodyDeclarationMap(nowClassDeclaration, MethodDeclaration.class); for (Entry<String, MethodDeclaration> oldEntry : oldMethodDeclarationMap.entrySet()) { String methodName = oldEntry.getKey(); if (!nowMethodDeclarationMap.containsKey(methodName)) { nowClassDeclaration.getMembers().add(oldEntry.getValue()); replaced = true; GLogger.println("方法(Method):" + methodName + " 被保留<<<<<<<<<<<<<<<<<<<<<<"); } } //接口,继承加进来 copyExtendsAndImpliments(oldClassDeclaration, nowClassDeclaration); } } } finally { if (replaced) { nowJavaUnitCharArrayWriter.reset(); nowJavaUnitCharArrayWriter.write(nowCompilationUnit.toString()); } } } private static void copyExtendsAndImpliments(ClassOrInterfaceDeclaration oldClassDeclaration, ClassOrInterfaceDeclaration nowClassDeclaration) { List<ClassOrInterfaceType> oldExtends = oldClassDeclaration.getExtendedTypes(); List<ClassOrInterfaceType> nowExtends = nowClassDeclaration.getExtendedTypes(); Set<String> nowExtendNames = classOrInterfaceTypesToNameSet(nowExtends); if (CollectionUtil.isNotEmpty(oldExtends)) { for (ClassOrInterfaceType classOrInterfaceType : oldExtends) { String name = classOrInterfaceType.getNameAsString(); if (!nowExtendNames.contains(name)) { nowClassDeclaration.addExtendedType(classOrInterfaceType); } } } List<ClassOrInterfaceType> oldImplements = oldClassDeclaration.getImplementedTypes(); List<ClassOrInterfaceType> nowImplements = nowClassDeclaration.getImplementedTypes(); Set<String> nowImplementNames = classOrInterfaceTypesToNameSet(nowImplements); if (CollectionUtil.isNotEmpty(oldImplements)) { for (ClassOrInterfaceType classOrInterfaceType : oldImplements) { String name = classOrInterfaceType.getNameAsString(); if (!nowImplementNames.contains(name)) { nowClassDeclaration.addImplementedType(classOrInterfaceType); } } } } public static Set<String> classOrInterfaceTypesToNameSet(List<ClassOrInterfaceType> types) { Set<String> nameSet = new HashSet<String>(); if (CollectionUtil.isNotEmpty(types)) { for (ClassOrInterfaceType classOrInterfaceType : types) { nameSet.add(classOrInterfaceType.getNameAsString()); } } return nameSet; } }