content: package com.docapis.core;
  
  import com.docapis.core.exception.JavaFileNotFoundException;
  import com.docapis.core.parser.*;
  import com.github.javaparser.JavaParser;
  import com.github.javaparser.ParseResult;
  import com.github.javaparser.ast.Modifier;
  import com.github.javaparser.ast.*;
  import com.github.javaparser.ast.body.Parameter;
  import com.github.javaparser.ast.body.*;
  import com.github.javaparser.ast.expr.AnnotationExpr;
  import com.github.javaparser.ast.expr.MemberValuePair;
  import com.github.javaparser.ast.expr.NormalAnnotationExpr;
  import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr;
  import com.github.javaparser.ast.type.ArrayType;
  import com.github.javaparser.ast.type.ClassOrInterfaceType;
  import com.github.javaparser.ast.type.Type;
  import com.github.javaparser.ast.type.TypeParameter;
  import org.w3c.dom.Document;
  
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  import java.io.*;
  import java.lang.annotation.Annotation;
  import java.lang.reflect.*;
  import java.util.*;
  import java.util.jar.JarEntry;
  import java.util.jar.JarFile;
  
  import static com.docapis.core.constant.CoreConstants.*;
  
  
  /**
   * parse utils
   * <p>
   * licence Apache 2.0,AGPL-3.0, from japidoc and doc-apis originated
   **/
  public class ParseUtils {
  
      /**
       * means a model class type
       */
      private static final String TYPE_MODEL = "_object";
  
      public static File searchJavaFile(File inJavaFile, String className) {
          File file = null;
  
          for (String javaSrcPath : DocContext.getJavaSrcPaths()) {
              file = searchJavaFileInner(javaSrcPath, inJavaFile, className);
              if (file != null) {
                  break;
              }
          }
  
          // 解决三方pageHelper插件无法读取问题
          if (file == null && PAGE_INFO_NAME.equals(className)) {
              file = Utils.getThirdPartJarFile(PAGE_INFO_FILE);
          }
  
          // easy-es分页器适配
          if (file == null && ES_PAGE_INFO_NAME.equals(className)) {
              file = Utils.getThirdPartJarFile(ES_PAGE_INFO_FILE);
          }
  
          // 最后的针扎,去目标地址搜索,提供可拓展性,比如有些项目可能用的是自己封装的分页插件,并且打到jar中了,需要开放此能力让这种项目开发者自行适配
          if (file == null) {
              file = getFileFromCurrentProjectPath(className + JAVA_FILE_SUFFIX);
          }
  
          if (file == null) {
              throw new JavaFileNotFoundException("Cannot find java file , in java file : " + inJavaFile.getAbsolutePath() + ", className : " + className);
          }
  
          return file;
      }
  
      private static File searchJavaFileInner(String javaSrcPath, File inJavaFile, String className) {
          CompilationUnit compilationUnit = compilationUnit(inJavaFile);
  
          String[] cPaths;
  
          Optional<ImportDeclaration> idOp = compilationUnit.getImports()
                  .stream()
                  .filter(im -> im.getNameAsString().endsWith("." + className))
                  .findFirst();
  
          //found in import
          if (idOp.isPresent()) {
              cPaths = idOp.get().getNameAsString().split("\\.");
              return backTraceJavaFileByName(javaSrcPath, cPaths);
          }
  
          //inner class in this file
          if (getInnerClassNode(compilationUnit, className).isPresent()) {
              return inJavaFile;
          }
  
          cPaths = className.split("\\.");
  
          //current directory
          if (cPaths.length == 1) {
  
              File[] javaFiles = inJavaFile.getParentFile().listFiles(new FilenameFilter() {
                  @Override
                  public boolean accept(File dir, String name) {
                      return name.equals(className + ".java");
                  }
              });
  
              if (javaFiles != null && javaFiles.length == 1) {
                  return javaFiles[0];
              }
  
          } else {
  
              final String firstPath = cPaths[0];
              //same package inner class
              File[] javaFiles = inJavaFile.getParentFile().listFiles(new FilenameFilter() {
                  @Override
                  public boolean accept(File dir, String name) {
                      int i = name.lastIndexOf(".java");
                      if (i == -1) {
                          return false;
                      }
                      return name.substring(0, i).equals(firstPath);
                  }
              });
  
              if (javaFiles != null && javaFiles.length > 0) {
                  File javaFile = javaFiles[0];
                  if (getInnerClassNode(compilationUnit(javaFile), className).isPresent()) {
                      return javaFile;
                  }
              }
          }
  
          //maybe a complete class name
          File javaFile = backTraceJavaFileByName(javaSrcPath, cPaths);
          if (javaFile != null) {
              return javaFile;
          }
  
          //.* at import
          NodeList<ImportDeclaration> importDeclarations = compilationUnit.getImports();
          if (importDeclarations.isNonEmpty()) {
              for (ImportDeclaration importDeclaration : importDeclarations) {
                  if (importDeclaration.toString().contains(".*")) {
                      String packageName = importDeclaration.getNameAsString();
                      cPaths = (packageName + "." + className).split("\\.");
                      javaFile = backTraceJavaFileByName(javaSrcPath, cPaths);
                      if (javaFile != null) {
                          break;
                      }
                  }
              }
          }
  
          // inner class in other package
          if (cPaths.length > 1) {
              try {
                  File innerClassFile = searchJavaFile(inJavaFile, cPaths[cPaths.length - 2]);
                  if (getInnerClassNode(compilationUnit(innerClassFile), cPaths[cPaths.length - 1]).isPresent()) {
                      return innerClassFile;
                  }
              } catch (JavaFileNotFoundException ex) {
                  // just ignore
              }
          }
  
          return javaFile;
      }
  
      private static Optional<TypeDeclaration> getInnerClassNode(CompilationUnit compilationUnit, String className) {
          return compilationUnit.findAll(TypeDeclaration.class)
                  .stream()
                  .filter(c -> c instanceof ClassOrInterfaceDeclaration || c instanceof EnumDeclaration)
                  .filter(c -> className.equals(c.getNameAsString()))
                  .findFirst();
      }
  
      private static File backTraceJavaFileByName(String javaSrcPath, String[] cPaths) {
          if (cPaths.length == 0) {
              return null;
          }
          String javaFilePath = javaSrcPath + Utils.joinArrayString(cPaths, "/") + ".java";
          File javaFile = new File(javaFilePath);
          if (javaFile.exists() && javaFile.isFile()) {
              return javaFile;
          } else {
              return backTraceJavaFileByName(javaSrcPath, Arrays.copyOf(cPaths, cPaths.length - 1));
          }
      }
  
      public static CompilationUnit compilationUnit(File javaFile) {
          try {
              JavaParser javaParser = new JavaParser();
              ParseResult<CompilationUnit> result = javaParser.parse(javaFile);
              return result.getResult().get();
          } catch (FileNotFoundException e) {
              throw new RuntimeException("java file not exits , file path : " + javaFile.getAbsolutePath());
          } catch (Exception e) {
              throw new RuntimeException("parser error , file path : " + javaFile.getAbsolutePath());
          }
      }
  
      public static void parseClassNodeByType(File inJavaFile, ClassNode rootClassNode, Type classType) {
  
          if (classType.getParentNode().isPresent()
                  && classType.getParentNode().get() instanceof ArrayType) {
              rootClassNode.setList(true);
          } else if (classType instanceof ArrayType) {
              rootClassNode.setList(true);
              classType = ((ArrayType) classType).getComponentType();
          } else if (isCollectionType(classType.asString())) {
              rootClassNode.setList(true);
              rootClassNode.setGenericNodes(null);
              List<ClassOrInterfaceType> collectionType = classType.getChildNodesByType(ClassOrInterfaceType.class);
              if (collectionType.isEmpty()) {
                  LogUtils.warn("We found Collection without specified Class Type, Please check ! java file : %s", inJavaFile.getName());
                  rootClassNode.setClassName("Object");
                  return;
              } else {
                  classType = collectionType.get(0);
              }
          }
  
          String unifyClassType = unifyType(classType.asString());
          if (TYPE_MODEL.equals(unifyClassType)) {
              if (classType instanceof ClassOrInterfaceType) {
  
                  String className = ((ClassOrInterfaceType) classType).getName().getIdentifier();
                  rootClassNode.setClassName(className);
  
                  try {
                      File modelJavaFile = searchJavaFile(inJavaFile, className);
                      rootClassNode.setClassFileName(modelJavaFile.getAbsolutePath());
                      parseClassNode(modelJavaFile, rootClassNode);
                  } catch (JavaFileNotFoundException ex) {
                      parseResponseNodeByReflection(inJavaFile, className, rootClassNode);
                  }
              }
          } else {
              rootClassNode.setClassName(unifyClassType);
          }
      }
  
      public static void parseClassNode(File modelJavaFile, ClassNode classNode) {
          innerParseClassNode(modelJavaFile, classNode);
      }
  
      private static void innerParseClassNode(File modelJavaFile, ClassNode classNode) {
          String resultClassName = classNode.getClassName();
          ParseUtils.compilationUnit(modelJavaFile).
                  findAll(ClassOrInterfaceDeclaration.class).
                  stream().filter(f -> resultClassName.endsWith(f.getNameAsString())).findFirst().ifPresent(cl -> {
  
                      //handle generic type
                      NodeList<TypeParameter> typeParameters = cl.getTypeParameters();
                      if (typeParameters.isNonEmpty() && classNode.getGenericNodes().size() == typeParameters.size()) {
                          for (int i = 0, len = typeParameters.size(); i != len; i++) {
                              // <T> or  <T extends Serializable>
                              classNode.getGenericNode(i).setPlaceholder(typeParameters.get(i).getName().getIdentifier());
                          }
                      }
  
                      NodeList<ClassOrInterfaceType> extendClassTypeList = cl.getExtendedTypes();
  
                      // 解析继承类
                      if (!extendClassTypeList.isEmpty()) {
                          // 子类传递泛型给父类的情形 class C<T> extends B<T> 或者 class C<T> extends B<Student>
                          ClassNode extendClassNode = new ClassNode();
                          List<GenericNode> extendGenericNodes = new ArrayList<>();
                          extendClassNode.setGenericNodes(extendGenericNodes); // 使用新的泛型列表
                          ClassOrInterfaceType extendClassType = extendClassTypeList.get(0);
                          extendClassType.getTypeArguments().ifPresent(typeList -> typeList.forEach(argType -> {
                              GenericNode extendGenericNode = new GenericNode();
                              GenericNode thisGenericNode = classNode.getGenericNode(argType.asString());
                              if (thisGenericNode != null) { // 拷贝一份，防止placeholder发生变化
                                  extendGenericNode.setFromJavaFile(thisGenericNode.getFromJavaFile());
                                  extendGenericNode.setClassType(thisGenericNode.getClassType());
                              } else {
                                  extendGenericNode.setFromJavaFile(modelJavaFile);
                                  extendGenericNode.setClassType(argType);
                              }
                              extendGenericNodes.add(extendGenericNode);
                          }));
                          ParseUtils.parseClassNodeByType(modelJavaFile, extendClassNode, extendClassType);
                          // 把解析结果复制到子类
                          classNode.getChildNodes().addAll(extendClassNode.getChildNodes());
                      }
  
                      cl.findAll(FieldDeclaration.class)
                              .stream().filter(fd -> !fd.getModifiers().contains(Modifier.staticModifier()))
                              .forEach(fd -> {
  
                                  //内部类字段也会读取到，这里特殊处理
                                  Node cClNode = fd.getParentNode().get();
  
                                  if (cClNode instanceof TypeDeclaration) {
                                      TypeDeclaration cClDeclaration = (TypeDeclaration) cClNode;
                                      if (!(resultClassName.equals(cClDeclaration.getNameAsString())
                                              || resultClassName.endsWith("." + cClDeclaration.getNameAsString()))) {
                                          return;
                                      }
                                  }
  
                                  //忽略字段
                                  if (fd.getAnnotationByName(DocIgnore.class.getSimpleName()).isPresent()) {
                                      return;
                                  }
  
                                  final boolean notNull = isFieldNotNull(fd);
  
                                  fd.getVariables().forEach(field -> {
                                      FieldNode fieldNode = new FieldNode();
                                      fieldNode.setNotNull(notNull);
                                      fieldNode.setClassNode(classNode);
  
                                      classNode.addChildNode(fieldNode);
                                      fd.getComment().ifPresent(c -> fieldNode.setDescription(Utils.cleanCommentContent(c.getContent())));
  
                                      if (!Utils.isNotEmpty(fieldNode.getDescription())) {
                                          field.getComment().ifPresent(c -> fieldNode.setDescription(Utils.cleanCommentContent(c.getContent())));
                                      }
  
                                      fd.getAnnotationByName("RapMock").ifPresent(an -> {
                                          if (an instanceof NormalAnnotationExpr) {
                                              NormalAnnotationExpr normalAnExpr = (NormalAnnotationExpr) an;
                                              MockNode mockNode = new MockNode();
                                              for (MemberValuePair mvPair : normalAnExpr.getPairs()) {
                                                  String name = mvPair.getName().asString();
                                                  if ("limit".equalsIgnoreCase(name)) {
                                                      mockNode.setLimit(Utils.removeQuotations(mvPair.getValue().toString()));
                                                  } else if ("value".equalsIgnoreCase(name)) {
                                                      mockNode.setValue(Utils.removeQuotations(mvPair.getValue().toString()));
                                                  }
                                              }
                                              fieldNode.setMockNode(mockNode);
                                          } else if (an instanceof SingleMemberAnnotationExpr) {
                                              SingleMemberAnnotationExpr singleAnExpr = (SingleMemberAnnotationExpr) an;
                                              MockNode mockNode = new MockNode();
                                              mockNode.setValue(Utils.removeQuotations(singleAnExpr.getMemberValue().toString()));
                                              fieldNode.setMockNode(mockNode);
                                          }
                                      });
  
                                      fieldNode.setName(field.getNameAsString());
  
                                      Type fieldType = fd.getElementType();
                                      parseFieldNode(fieldNode, modelJavaFile, fieldType);
                                  });
                              });
                  });
  
          //恢复原来的名称
          classNode.setClassName(resultClassName);
      }
  
      private static boolean isFieldNotNull(FieldDeclaration fd) {
          for (AnnotationExpr annotationExpr : fd.getAnnotations()) {
              if (isNotNullAnnotation(annotationExpr.getNameAsString())) {
                  return true;
              }
          }
          return false;
      }
  
      private static void parseFieldNode(FieldNode fieldNode, File inJavaFile, Type fieldType) {
  
          final GenericNode genericNode = fieldNode.getClassNode().getGenericNode(fieldType.asString());
  
          //Enum
          if (genericNode == null && !fieldType.asString().contains("<")) {
              final String fieldClassType = fieldType.asString();
              if (TYPE_MODEL.equals(unifyType(fieldClassType))) {
  
                  Optional<EnumDeclaration> ed = null;
                  try {
                      File childJavaFile = searchJavaFile(inJavaFile, fieldClassType);
                      ed = compilationUnit(childJavaFile)
                              .getChildNodesByType(EnumDeclaration.class)
                              .stream()
                              .filter(em -> fieldClassType.endsWith(em.getNameAsString()))
                              .findFirst();
                  } catch (JavaFileNotFoundException ex) {
                      LogUtils.info("we think %s should not be an enum type", fieldClassType);
                  }
  
                  if (ed != null && ed.isPresent()) {
                      fieldNode.setType("enum");
                      List<EnumConstantDeclaration> constants = ed.get().getChildNodesByType(EnumConstantDeclaration.class);
                      StringBuilder sb = new StringBuilder(fieldNode.getDescription() == null ? "" : fieldNode.getDescription());
                      sb.append(" [");
                      for (int i = 0, size = constants.size(); i != size; i++) {
                          sb.append(constants.get(i).getNameAsString());
                          if (i != size - 1) {
                              sb.append(",");
                          }
                      }
                      sb.append("]");
                      fieldNode.setDescription(sb.toString());
                      return;
                  }
              }
          }
  
          // generic type field, do replacement
          if (genericNode != null) {
              fieldType = genericNode.getClassType();
              inJavaFile = genericNode.getFromJavaFile();
          }
  
          Boolean isList;
  
          if (fieldType.getParentNode().get() instanceof ArrayType) {
              isList = true;
          } else if (fieldType instanceof ArrayType) {
              isList = true;
              fieldType = ((ArrayType) fieldType).getComponentType();
              GenericNode arrayTypeNode = fieldNode.getClassNode().getGenericNode(fieldType.asString());
              if (arrayTypeNode != null) {
                  fieldType = arrayTypeNode.getClassType();
                  inJavaFile = arrayTypeNode.getFromJavaFile();
              }
          } else {
              if (isCollectionType(fieldType.asString())) {
                  isList = true;
                  List<ClassOrInterfaceType> collectionType = fieldType.getChildNodesByType(ClassOrInterfaceType.class);
                  if (collectionType.isEmpty()) {
                      LogUtils.warn("We found Collection without specified Class Type, Please check ! java file : %s", inJavaFile.getName());
                      fieldNode.setType("Object[]");
                      return;
                  } else {
                      // 是否在泛型列表中
                      GenericNode collectionGenericNode = fieldNode.getClassNode().getGenericNode(collectionType.get(0).asString());
                      if (collectionGenericNode != null) {
                          fieldType = collectionGenericNode.getClassType();
                          inJavaFile = collectionGenericNode.getFromJavaFile();
                      } else {
                          fieldType = collectionType.get(0);
                      }
                  }
              } else {
                  isList = false;
              }
          }
  
          String fieldClassType;
  
          if (fieldType instanceof ClassOrInterfaceType) {
              fieldClassType = ((ClassOrInterfaceType) fieldType).getName().getIdentifier();
          } else {
              fieldClassType = fieldType.asString();
          }
  
          final String unifyType = unifyType(fieldClassType);
  
          if (TYPE_MODEL.equals(unifyType)) {
  
              ClassNode childNode = new ClassNode();
              childNode.setParentNode(fieldNode.getClassNode());
              childNode.setList(isList);
              childNode.setClassName(fieldClassType);
  
              fieldNode.setChildNode(childNode);
              fieldNode.setType(isList ? fieldClassType + "[]" : fieldClassType);
  
              final File childJavaFile = inJavaFile;
              if (fieldType instanceof ClassOrInterfaceType) {
                  ((ClassOrInterfaceType) fieldType).getTypeArguments().ifPresent(typeList -> typeList.forEach(argType -> {
                      GenericNode childClassGenericNode = new GenericNode();
  
                      if (argType instanceof ArrayType) {
                          GenericNode arrayTypeNode = fieldNode.getClassNode().getGenericNode(((ArrayType) argType).getComponentType().asString());
                          if (arrayTypeNode != null) {
                              ((ArrayType) argType).setComponentType(arrayTypeNode.getClassType());
                          }
                      } else {
                          GenericNode arrayTypeNode = fieldNode.getClassNode().getGenericNode(argType.asString());
                          if (arrayTypeNode != null) {
                              argType = arrayTypeNode.getClassType();
                          }
                      }
  
                      childClassGenericNode.setClassType(argType);
                      childClassGenericNode.setFromJavaFile(childJavaFile);
                      childNode.addGenericNode(childClassGenericNode);
                  }));
              } else if (fieldType instanceof com.github.javaparser.ast.type.WildcardType) {
                  LogUtils.error("Parameter type not supporting wildcards like '?', skip generate %s", inJavaFile.toString());
              }
  
              try {
                  File childNodeJavaFile = searchJavaFile(inJavaFile, fieldClassType);
                  childNode.setClassFileName(childNodeJavaFile.getAbsolutePath());
                  if (!inClassDependencyTree(fieldNode, fieldNode.getClassNode())) {
                      parseClassNode(childNodeJavaFile, childNode);
                  } else {
                      fieldNode.setLoopNode(Boolean.TRUE);
                  }
              } catch (JavaFileNotFoundException ex) {
                  LogUtils.warn(ex.getMessage() + ", we cannot found more information of it, you've better to make it a JavaBean");
                  fieldNode.setType(isList ? "Object[]" : "Object");
              }
          } else {
              fieldNode.setType(isList ? unifyType + "[]" : unifyType);
          }
      }
  
      private static boolean inClassDependencyTree(FieldNode fieldNode, ClassNode parentClassNode) {
  
          if (fieldNode.getChildNode().getModelClass() != null && parentClassNode.getModelClass() != null) {
              if (fieldNode.getChildNode().getModelClass().equals(parentClassNode.getModelClass())) {
                  return true;
              }
          } else if (fieldNode.getChildNode().getClassFileName() != null) {
              if (fieldNode.getChildNode().getClassFileName()
                      .equals(parentClassNode.getClassFileName())
                      && fieldNode.getChildNode().getClassName().equals(parentClassNode.getClassName())) {
                  return true;
              }
          }
  
          if (parentClassNode.getParentNode() == null) {
              return false;
          }
  
          return inClassDependencyTree(fieldNode, parentClassNode.getParentNode());
      }
  
      public static boolean isModelType(String className) {
          if (className == null) {
              return false;
          }
          return TYPE_MODEL.equals(unifyType(className));
      }
  
      public static boolean isEnum(File inJavaFile, String className) {
          try {
              File javaFile = searchJavaFile(inJavaFile, className);
              return compilationUnit(javaFile).getEnumByName(className).isPresent();
          } catch (Exception ex) {
              return false;
          }
      }
  
      public static String unifyType(String className) {
          String[] cPaths = className.replace("[]", "").split("\\.");
          String rawType = cPaths[cPaths.length - 1];
          if ("byte".equalsIgnoreCase(rawType)) {
              return "byte";
          } else if ("short".equalsIgnoreCase(rawType)) {
              return "short";
          } else if ("int".equalsIgnoreCase(rawType)
                  || "Integer".equalsIgnoreCase(rawType)
                  || "BigInteger".equalsIgnoreCase(rawType)) {
              return "int";
          } else if ("long".equalsIgnoreCase(rawType)) {
              return "long";
          } else if ("float".equalsIgnoreCase(rawType)) {
              return "float";
          } else if ("double".equalsIgnoreCase(rawType)
                  || "BigDecimal".equalsIgnoreCase(rawType)) {
              return "double";
          } else if ("boolean".equalsIgnoreCase(rawType)) {
              return "boolean";
          } else if ("char".equalsIgnoreCase(rawType)
                  || "Character".equalsIgnoreCase(rawType)) {
              return "char";
          } else if ("String".equalsIgnoreCase(rawType)) {
              return "string";
          } else if ("date".equalsIgnoreCase(rawType)
                  || "LocalDate".equalsIgnoreCase(rawType)
                  || "ZonedDateTime".equalsIgnoreCase(rawType) || "LocalDateTime".equals(rawType)) {
              return "date";
          } else if ("file".equalsIgnoreCase(rawType) || "MultipartFile".equalsIgnoreCase(rawType)) {
              return "file";
          } else if ("Object".equalsIgnoreCase(rawType)) {
              return "object";
          } else if ("enum".equalsIgnoreCase(rawType)) {
              return "enum";
          } else {
              return TYPE_MODEL;
          }
      }
  
      public static boolean isCollectionType(String className) {
          String[] cPaths = className.split("\\.");
  
          String genericType = cPaths[cPaths.length - 1];
  
          //fix List<Demo1.Demo2>
          for (String cPath : cPaths) {
              if (cPath.contains("<")) {
                  genericType = cPath;
              }
          }
  
          int genericLeftIndex = genericType.indexOf("<");
          String rawType = genericLeftIndex != -1 ? genericType.substring(0, genericLeftIndex) : genericType;
          String collectionClassName = "java.util." + rawType;
          try {
              Class collectionClass = Class.forName(collectionClassName);
              return Collection.class.isAssignableFrom(collectionClass);
          } catch (ClassNotFoundException e) {
              return false;
          }
      }
  
      public static boolean isExcludeParam(Parameter param) {
          final String type = param.getTypeAsString();
          return type.equals("HttpServletRequest")
                  || type.equals("HttpServletResponse")
                  || type.equals("HttpSession");
      }
  
      public static Class getClassInJavaFile(File inJavaFile, String className) {
          Class modelClass = null;
          CompilationUnit compilationUnit = compilationUnit(inJavaFile);
          for (ImportDeclaration importDeclaration : compilationUnit.getImports()) {
              String importName = importDeclaration.getNameAsString();
              if (importName.endsWith("." + className)) {
                  try {
                      modelClass = Class.forName(importName);
                      break;
                  } catch (Exception e) {
                      LogUtils.error("ClassNotFoundException: " + className, e);
                  }
              } else if (importName.endsWith(".*")) {
                  try {
                      modelClass = Class.forName(importName.replace("*", className));
                  } catch (Exception e) {
                      // just ignore
                  }
              }
          }
          try {
              modelClass = Class.forName(compilationUnit.getPackageDeclaration().get().getNameAsString().concat(".").concat(className));
          } catch (Exception e) {
              // just ignore
          }
          return modelClass;
      }
  
  
      private static void parseResponseNodeByReflection(File inJavaFile, String className, ClassNode classNode) {
  
          Class modelClass = getClassInJavaFile(inJavaFile, className);
  
          if (modelClass != null) {
  
              if (classNode instanceof ResponseNode) {
  
                  ResponseNode responseNode = (ResponseNode) classNode;
                  responseNode.reset();
  
                  // 解析方法返回值泛型信息
                  Class controllerClass = ParseUtils.getClassInJavaFile(inJavaFile, inJavaFile.getName().replace(".java", ""));
                  if (controllerClass != null) {
                      RequestNode requestNode = responseNode.getRequestNode();
                      try {
                          // 获取该api对应的请求方法
                          Method apiMethod = null;
  
                          for (Method method : controllerClass.getDeclaredMethods()) {
                              if (method.getName().equals(requestNode.getMethodName())) {
                                  if (method.getAnnotations() != null) {
                                      for (Annotation annotation : method.getAnnotations()) {
                                          String reqUrl = requestNode.getUrl();
                                          if (reqUrl.contains("/")) {
                                              reqUrl = reqUrl.substring(reqUrl.lastIndexOf("/") + 1);
                                          }
                                          if (annotation.toString().contains(reqUrl)) {
                                              apiMethod = method;
                                          }
                                      }
                                  }
                              }
                          }
  
                          if (apiMethod != null) {
                              java.lang.reflect.Type returnType = apiMethod.getGenericReturnType();
                              //如果是ResponseEntity, 则取里面一层
                              if (apiMethod.getReturnType().getName().equals("org.springframework.http.ResponseEntity")) {
                                  java.lang.reflect.Type[] responseEntityTypeArguments = ((ParameterizedType) apiMethod.getGenericReturnType()).getActualTypeArguments();
                                  if (responseEntityTypeArguments.length == 1) {
                                      if (responseEntityTypeArguments[0] instanceof ParameterizedType) {
                                          ParameterizedType parameterizedType = (ParameterizedType) responseEntityTypeArguments[0];
                                          if (parameterizedType.getRawType() instanceof Class) {
                                              ParseUtils.parseGenericNodesInType((Class) parameterizedType.getRawType(), parameterizedType, responseNode.getGenericNodes());
                                          }
                                      }
                                  }
                              } else {
                                  ParseUtils.parseGenericNodesInType(apiMethod.getReturnType(), returnType, responseNode.getGenericNodes());
                              }
  
                          }
  
                      } catch (Exception e2) {
                          LogUtils.error("get method error", e2);
                      }
                  }
              }
  
              // 使用反射解析字段
              if (DocContext.getDocsConfig().getOpenReflection()) {
                  classNode.setModelClass(modelClass);
                  parseClassNodeByReflection(classNode);
              }
          }
      }
  
      public static void parseClassNodeByReflection(ClassNode classNode) {
  
          Class modelClass = classNode.getModelClass();
  
          // java 内部对象或者接口，直接忽略
          if (modelClass.getPackage().getName().startsWith("java.") || modelClass.isInterface()) {
              return;
          }
  
          List<FieldNode> childNodes = classNode.getChildNodes();
          classNode.setClassName(modelClass.getName());
  
          for (Field field : modelClass.getDeclaredFields()) {
  
              // 过滤 static
              if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
                  continue;
              }
  
              FieldNode fieldNode = new FieldNode();
              childNodes.add(fieldNode);
  
              Annotation[] annotations = field.getAnnotations();
              if (annotations != null) {
                  for (Annotation annotation : annotations) {
                      if (isNotNullAnnotation(annotation.getClass().getSimpleName())) {
                          fieldNode.setNotNull(true);
                          break;
                      }
                  }
              }
  
              fieldNode.setName(field.getName());
  
              // 枚举,当做字符串类型
              if (field.getType().isEnum()) {
                  fieldNode.setType("enum");
                  continue;
              }
  
              ClassNode childClassNode = new ClassNode();
              childClassNode.setParentNode(classNode);
              Class childNodeClass = null;
              java.lang.reflect.Type childGenericType = null;
  
              Class fieldClass = field.getType();
  
              //数组
              if (fieldClass.isArray()) {
                  Class componentType = fieldClass.getComponentType();
                  final String unifyFieldType = unifyType(componentType.getSimpleName());
                  fieldNode.setType(unifyFieldType + "[]");
                  if (unifyFieldType.equals(TYPE_MODEL)) {
                      childClassNode.setList(true);
                      childNodeClass = componentType;
                      childGenericType = field.getGenericType();
                  } else {
                      continue;
                  }
              }
  
              //列表
              if (Collection.class.isAssignableFrom(fieldClass)) {
                  fieldNode.setType("[]");
  
                  java.lang.reflect.Type genericType = field.getGenericType();
                  if (!(genericType instanceof ParameterizedType)) {
                      continue;
                  }
  
                  java.lang.reflect.Type boxType = ((ParameterizedType) genericType).getActualTypeArguments()[0];
                  childGenericType = boxType;
  
                  Class boxClass = null;
                  if (boxType instanceof ParameterizedType) {  // List<GenericResult<Student[], Integer>> picList
                      boxClass = (Class) ((ParameterizedType) boxType).getRawType();
                  } else if (boxType instanceof TypeVariable) { // List<T>
                      GenericNode genericNode = classNode.getGenericNode(((TypeVariable) boxType).getName());
                      if (genericNode == null) {
                          continue;
                      } else {
                          boxClass = genericNode.getModelClass();
                          childClassNode.setGenericNodes(genericNode.getChildGenericNode());
                      }
                  } else if (boxType instanceof Class) { // List<String>
                      boxClass = (Class) boxType;
                  }
  
                  if (boxClass == null) {
                      continue;
                  }
  
                  final String unifyFieldType = unifyType(boxClass.getSimpleName());
                  if (unifyFieldType.equals(TYPE_MODEL)) {
                      childClassNode.setList(true);
                      childNodeClass = boxClass;
                  } else {
                      fieldNode.setType(unifyFieldType + "[]");
                      continue;
                  }
              }
  
  
              if (childNodeClass == null) {
                  childNodeClass = fieldClass;
                  childGenericType = field.getGenericType();
  
                  //V object
                  if (childGenericType instanceof TypeVariable) {
                      GenericNode genericNode = classNode.getGenericNode(((TypeVariable) childGenericType).getName());
                      if (genericNode == null) {
                          continue;
                      } else {
                          childNodeClass = genericNode.getModelClass();
                          childClassNode.setGenericNodes(genericNode.getChildGenericNode());
                      }
                  } else if (childGenericType instanceof ParameterizedType) {
                      java.lang.reflect.Type[] paramTypes = ((ParameterizedType) childGenericType).getActualTypeArguments();
                      for (java.lang.reflect.Type paramType : paramTypes) {
                          if (paramType instanceof TypeVariable) {
                              GenericNode genericNode = classNode.getGenericNode(((TypeVariable) paramType).getName());
                              if (genericNode != null) {
                                  childClassNode.addGenericNode(genericNode);
                              }
                          }
                      }
                  }
  
                  final String unifyFieldType = unifyType(childNodeClass.getSimpleName());
                  if (!unifyFieldType.equals(TYPE_MODEL)) {
                      fieldNode.setType(unifyFieldType);
                      continue;
                  }
              }
  
              // 解析泛型
              parseGenericNodesInType(childNodeClass, childGenericType, childClassNode.getGenericNodes());
              childClassNode.setClassName(childNodeClass.getName());
              childClassNode.setModelClass(childNodeClass);
              fieldNode.setChildNode(childClassNode);
  
              if (!inClassDependencyTree(fieldNode, classNode)) {
                  parseClassNodeByReflection(childClassNode);
              }
          }
  
          // 解析父类
          Class superClass = modelClass.getSuperclass();
  
          if (superClass != null) {
              java.lang.reflect.Type superType = modelClass.getGenericSuperclass();
  
              // 父类泛型信息
              if (superType instanceof ParameterizedType) {
                  classNode.getGenericNodes().clear();
                  parseGenericNodesInType(superClass, superType, classNode.getGenericNodes());
              }
  
              classNode.setModelClass(modelClass.getSuperclass());
              parseClassNodeByReflection(classNode);
  
              //恢复
              classNode.setClassName(modelClass.getName());
              classNode.setModelClass(modelClass);
          }
      }
  
      public static void parseGenericNodesInType(Class fieldClass, java.lang.reflect.Type fieldGenericType, List<GenericNode> genericNodeList) {
  
          if (fieldGenericType instanceof GenericArrayType) {
              fieldGenericType = ((GenericArrayType) fieldGenericType).getGenericComponentType();
          }
  
          if (fieldGenericType instanceof ParameterizedType) {
  
              TypeVariable[] typeVariables = fieldClass.getTypeParameters();
              java.lang.reflect.Type[] paramTypes = ((ParameterizedType) fieldGenericType).getActualTypeArguments();
  
              if (typeVariables.length == 0 || typeVariables.length != paramTypes.length) {
                  return;
              }
  
              int paramIndex = 0;
  
              for (java.lang.reflect.Type paramType : paramTypes) {
                  GenericNode paramGenericNode = new GenericNode();
                  final String placeholder = typeVariables[paramIndex++].getName();
                  paramGenericNode.setPlaceholder(placeholder);
  
                  if (paramType instanceof ParameterizedType) {  // GenericResult<Student, Integer> picList
                      Class boxClass = (Class) ((ParameterizedType) paramType).getRawType();
                      paramGenericNode.setModelClass(boxClass);
                      genericNodeList.add(paramGenericNode);
  
                      java.lang.reflect.Type[] childTypeArguments = ((ParameterizedType) paramType).getActualTypeArguments();
                      TypeVariable[] boxClassTypeVariables = boxClass.getTypeParameters();
  
                      if (childTypeArguments != null) {
  
                          if (childTypeArguments.length == 0 || childTypeArguments.length != boxClassTypeVariables.length) {
                              continue;
                          }
  
                          List<GenericNode> childGenericNodeList = new ArrayList<>();
                          paramGenericNode.setChildGenericNode(childGenericNodeList);
                          int childParamIndex = 0;
                          for (java.lang.reflect.Type childGenericType : childTypeArguments) {
                              if (childGenericType instanceof Class) {
                                  GenericNode childGenericNode = new GenericNode();
                                  childGenericNode.setModelClass((Class) childGenericType);
                                  childGenericNode.setPlaceholder(boxClassTypeVariables[childParamIndex++].getName());
                                  childGenericNodeList.add(childGenericNode);
                              } else {
                                  parseGenericNodesInType(boxClass, childGenericType, childGenericNodeList);
                              }
                          }
                      }
                  } else if (paramType instanceof Class) { // List<String>
                      Class boxClass = (Class) paramType;
                      paramGenericNode.setModelClass(boxClass);
                      genericNodeList.add(paramGenericNode);
                  }
              }
          }
      }
  
      public static boolean isNotNullAnnotation(String annotationName) {
          return annotationName.endsWith("NotEmpty")
                  || annotationName.endsWith("NotNull")
                  || annotationName.endsWith("NotBlank");
      }
  
      public static File getFileFromCurrentProjectPath(String fileName) {
          String path = System.getProperty(USER_DIR) + File.separator + fileName;
          File file = new File(path);
          if (file.exists()) {
              return file;
          } else {
              return null;
          }
      }
  }
  