diff --git a/pom.xml b/pom.xml
index c4f7e000..1a80c0a6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,6 +73,11 @@
slf4j-api
2.0.7
+
+ org.eclipse.jgit
+ org.eclipse.jgit
+ 5.13.2.202306221912-r
+
diff --git a/src/main/java/com/ly/doc/extension/dict/DictionaryValuesResolver.java b/src/main/java/com/ly/doc/extension/dict/DictionaryValuesResolver.java
index 58e98489..f5adce8d 100644
--- a/src/main/java/com/ly/doc/extension/dict/DictionaryValuesResolver.java
+++ b/src/main/java/com/ly/doc/extension/dict/DictionaryValuesResolver.java
@@ -22,7 +22,6 @@
import com.power.common.model.EnumDictionary;
-import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.Collections;
@@ -38,8 +37,7 @@ public interface DictionaryValuesResolver {
* @param clazz dictionary class
* @return the dictionary
*/
- @Nonnull
- default Collection resolve(@Nonnull Class> clazz) {
+ default Collection resolve(Class> clazz) {
return resolve();
}
@@ -48,7 +46,6 @@ default Collection resolve(@Nonnull Class> clazz
*
* @see #resolve(Class)
*/
- @Nonnull
default Collection resolve() {
return Collections.emptyList();
}
diff --git a/src/main/java/com/ly/doc/factory/BuildTemplateFactory.java b/src/main/java/com/ly/doc/factory/BuildTemplateFactory.java
index f1f0bb30..b439c92e 100644
--- a/src/main/java/com/ly/doc/factory/BuildTemplateFactory.java
+++ b/src/main/java/com/ly/doc/factory/BuildTemplateFactory.java
@@ -21,6 +21,7 @@
package com.ly.doc.factory;
import com.ly.doc.constants.FrameworkEnum;
+import com.ly.doc.model.IDoc;
import com.ly.doc.template.IDocBuildTemplate;
/**
@@ -35,10 +36,10 @@ public class BuildTemplateFactory {
* @param API doc type
* @return Implements of IDocBuildTemplate
*/
- public static IDocBuildTemplate getDocBuildTemplate(String framework) {
+ public static IDocBuildTemplate getDocBuildTemplate(String framework) {
String className = FrameworkEnum.getClassNameByFramework(framework);
try {
- return (IDocBuildTemplate) Class.forName(className).newInstance();
+ return (IDocBuildTemplate) Class.forName(className).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
diff --git a/src/main/java/com/ly/doc/helper/DocBuildHelper.java b/src/main/java/com/ly/doc/helper/DocBuildHelper.java
new file mode 100644
index 00000000..4d1d7bcc
--- /dev/null
+++ b/src/main/java/com/ly/doc/helper/DocBuildHelper.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2018-2023 smart-doc
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 com.ly.doc.helper;
+
+import com.ly.doc.builder.ProjectDocConfigBuilder;
+import com.ly.doc.model.ApiConfig;
+import com.ly.doc.model.IDoc;
+import com.ly.doc.model.IMethod;
+import com.ly.doc.model.dependency.ApiDependency;
+import com.ly.doc.model.dependency.DependencyTree;
+import com.ly.doc.model.dependency.FileDiff;
+import com.power.common.util.CollectionUtil;
+import com.power.common.util.StringUtil;
+import com.thoughtworks.qdox.JavaProjectBuilder;
+import com.thoughtworks.qdox.model.JavaClass;
+import com.thoughtworks.qdox.model.JavaType;
+import org.eclipse.jgit.diff.DiffEntry;
+
+import java.io.File;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * @author Fio
+ */
+public class DocBuildHelper {
+
+ private JavaProjectBuilder projectBuilder;
+
+ /**
+ * {@link ApiConfig#getCodePath()}
+ */
+ private String codePath;
+
+ private DependencyTree dependencyTree;
+
+ private final GitHelper gitHelper = GitHelper.create();
+
+ /**
+ * changed file list
+ * value set within {@link #getChangedFilesFromVCS(Predicate)}
+ * value get within {@link #mergeDependencyTree(List)}
+ */
+ private Set fileDiffList = Collections.emptySet();
+
+ private DocBuildHelper() {
+
+ }
+
+ public static DocBuildHelper create(ProjectDocConfigBuilder configBuilder) {
+ ApiConfig apiConfig = configBuilder.getApiConfig();
+
+ String baseDir = apiConfig.getBaseDir();
+ String codePath = apiConfig.getCodePath();
+
+ if (StringUtil.isEmpty(baseDir)) {
+ throw new RuntimeException("ERROR: The baseDir can't be empty.");
+ }
+ if (StringUtil.isEmpty(codePath)) {
+ throw new RuntimeException("ERROR: The codePath can't be empty.");
+ }
+
+ DocBuildHelper helper = new DocBuildHelper();
+ helper.projectBuilder = configBuilder.getJavaProjectBuilder();
+ helper.codePath = codePath;
+ helper.dependencyTree = DependencyTree.detect(baseDir);
+
+ return helper;
+ }
+
+ /**
+ * Read the dependency-tree-file from baseDir
+ *
+ * @return DependencyTree instance
+ */
+ public DependencyTree getDependencyTree() {
+ return dependencyTree;
+ }
+
+ private void writeDependencyTree(List dependencyTree) {
+ if (gitHelper.notGitRepo()) {
+ return;
+ }
+
+ String commitId = gitHelper.getLatestCommitId();
+
+ if (dependencyTree == null) {
+ dependencyTree = Collections.emptyList();
+ }
+
+ List mergedDependencyTree = mergeDependencyTree(dependencyTree);
+ this.dependencyTree.setConfig(commitId, mergedDependencyTree);
+
+ DependencyTree.write(this.dependencyTree);
+ }
+
+ private List mergeDependencyTree(List newDependencyTree) {
+ List oldDependencyTree = new ArrayList<>(this.dependencyTree.getDependencyTree());
+
+ // remove the deleted or deprecated dependencies
+ List deletedClazz = this.fileDiffList.stream()
+ // newQualifiedName equals /dev/null means the class is deleted
+ .filter(item -> "/dev/null".equals(item.getNewQualifiedName()))
+ .map(FileDiff::getOldQualifiedName)
+ .distinct()
+ .collect(Collectors.toList());
+ List newDependencyApiClasses = newDependencyTree.stream()
+ .map(ApiDependency::getClazz).distinct()
+ .collect(Collectors.toList());
+ List deprecatedClazz = this.fileDiffList.stream()
+ .filter(FileDiff::isEntryPoint)
+ .map(FileDiff::getNewQualifiedName)
+ .filter(item -> {
+ boolean contains = newDependencyApiClasses.contains(item);
+ if (contains) {
+ return false;
+ }
+
+ try {
+ // This logic is copied from RpcDocBuildTemplate#handleJavaApiDoc.
+ // Used for mark deprecated api class correctly.
+ JavaClass cls = projectBuilder.getClassByName(item);
+ List clsImplements = cls.getImplements();
+ if (CollectionUtil.isNotEmpty(clsImplements) && !cls.isInterface()) {
+ return clsImplements.stream()
+ .map(JavaType::getCanonicalName)
+ .noneMatch(newDependencyApiClasses::contains);
+ }
+ } catch (Exception ignore) {
+ }
+
+ return false;
+ })
+ .collect(Collectors.toList());
+ oldDependencyTree.removeIf(dependency ->
+ deletedClazz.contains(dependency.getClazz())
+ || deprecatedClazz.contains(dependency.getClazz())
+ || deprecatedClazz.stream().anyMatch(deprecate ->
+ dependency.getDerivedClazz().contains(deprecate))
+ );
+
+ // replace the old dependency tree with new dependency
+ oldDependencyTree.replaceAll(dependency -> {
+ String docClazz = dependency.getClazz();
+
+ ApiDependency apiDependency = newDependencyTree.stream()
+ .filter(newDependency -> docClazz.equals(newDependency.getClazz()))
+ .findFirst()
+ .orElse(dependency);
+
+ // replace and remove from newDependencyTree
+ newDependencyTree.removeIf(newDependency -> newDependency.equals(apiDependency));
+
+ return apiDependency;
+ });
+
+ // add new dependency
+ if (CollectionUtil.isNotEmpty(newDependencyTree)) {
+ oldDependencyTree.addAll(newDependencyTree);
+ }
+
+ return oldDependencyTree;
+ }
+
+ /**
+ * Find and gather classes and their dependencies.
+ *
+ * When a class is modified within the git tree, and it is part of an endpoint argument or return value,
+ * this method will also include the classes containing these endpoints classes.
+ *
+ * If all modified classes are not part of the API dependency tree (e.g., they are services or mappers),
+ * this method will return an empty collection, as they do not impact the API documentation.
+ */
+ public Set getChangedFilesFromVCS(Predicate isEntryPoint) {
+ String commitId = dependencyTree.getCommitId();
+ List diff = new ArrayList<>(gitHelper.getDiff(commitId));
+ Set uncommitted = new HashSet<>(gitHelper.getUncommitted());
+ Set untracked = new HashSet<>(gitHelper.getUntracked());
+
+ if (CollectionUtil.isEmpty(diff)
+ && CollectionUtil.isEmpty(uncommitted)
+ && CollectionUtil.isEmpty(untracked)) {
+ return Collections.emptySet();
+ }
+
+ Set fileDiffList = getChangedFiles(diff, uncommitted, untracked);
+ populateRelatedClazzAndMarkEntryPoint(fileDiffList, isEntryPoint);
+
+ this.fileDiffList = fileDiffList;
+
+ return fileDiffList;
+ }
+
+ private Set getChangedFiles(List diff, Set uncommitted, Set untracked) {
+ diff.removeIf(item -> !isSupportedSourceCodeType(item.getNewPath()));
+ uncommitted.removeIf(item -> !isSupportedSourceCodeType(item));
+ untracked.removeIf(item -> !isSupportedSourceCodeType(item));
+
+ Set diffList = new HashSet<>(diff.size() + uncommitted.size() + untracked.size());
+
+ // diff in git tree
+ diff.forEach(entry -> {
+ FileDiff fileDiff = new FileDiff();
+
+ String changeType = entry.getChangeType().name();
+ fileDiff.setChangeType(FileDiff.ChangeType.valueOf(changeType));
+ fileDiff.setOldQualifiedName(toQualifiedName(entry.getOldPath()));
+ fileDiff.setNewQualifiedName(toQualifiedName(entry.getNewPath()));
+
+ diffList.add(fileDiff);
+ });
+
+ // uncommitted changes
+ uncommitted.forEach(path -> {
+ FileDiff fileDiff = new FileDiff();
+
+ fileDiff.setChangeType(FileDiff.ChangeType.UNCOMMITTED);
+ fileDiff.setNewQualifiedName(toQualifiedName(path));
+
+ diffList.add(fileDiff);
+ });
+
+ // untracked changes
+ untracked.forEach(path -> {
+ FileDiff fileDiff = new FileDiff();
+
+ fileDiff.setChangeType(FileDiff.ChangeType.UNTRACKED);
+ fileDiff.setNewQualifiedName(toQualifiedName(path));
+
+ diffList.add(fileDiff);
+ });
+
+ return diffList;
+ }
+
+ /**
+ * convert the relative path from git to package
+ */
+ private String toQualifiedName(String relativePath) {
+ // /dev/null is git default path when a file is added or deleted
+ if ("/dev/null".equals(relativePath)) {
+ return relativePath;
+ }
+
+ int index = relativePath.indexOf(this.codePath);
+ if (index < 0) {
+ return relativePath;
+ }
+
+ String filePath = relativePath.substring(index + this.codePath.length() + 1);
+ if (StringUtil.isEmpty(filePath)) {
+ return relativePath;
+ }
+
+ if (isSupportedSourceCodeType(filePath)) {
+ int lastIndex = filePath.lastIndexOf(".");
+ filePath = filePath.substring(0, lastIndex);
+ }
+
+ return filePath.replace(File.separator, ".");
+ }
+
+ private boolean isSupportedSourceCodeType(String path) {
+ // maybe there's a better way...
+ return path.endsWith(".java")
+ || path.endsWith(".kt")
+ || path.endsWith(".groovy")
+ || path.endsWith(".scala");
+ }
+
+ private void populateRelatedClazzAndMarkEntryPoint(Set diffList, Predicate isEntryPoint) {
+ List oldDependencyTree = this.dependencyTree.getDependencyTree();
+
+ if (CollectionUtil.isEmpty(oldDependencyTree)) {
+ return;
+ }
+
+ // foreach the exist dependency tree,
+ // check whether it is entry point if clazzName is matched and get the related entry points
+ oldDependencyTree.forEach(dependency -> {
+ String clazz = dependency.getClazz();
+
+ Optional matchClazzOptional = diffList.stream()
+ .filter(item -> {
+ boolean equals = clazz.equals(item.getNewQualifiedName());
+ if (equals) {
+ return true;
+ }
+
+ List derivedClazz = dependency.getDerivedClazz();
+ if (CollectionUtil.isEmpty(derivedClazz)) {
+ return false;
+ }
+
+ return dependency.getDerivedClazz().contains(item.getNewQualifiedName());
+ })
+ .findFirst();
+ if (matchClazzOptional.isPresent()) {
+ // mark the class is entry point(maybe now is not)
+ matchClazzOptional.get().setEntryPoint(true);
+ return;
+ }
+
+ dependency.getApis().forEach(apiInfo -> {
+ boolean matchArgs = apiInfo.getArgs().stream()
+ .anyMatch(
+ item -> diffList.stream()
+ .anyMatch(
+ diff -> item.equals(diff.getNewQualifiedName())
+ )
+ );
+
+ boolean matchReturns = apiInfo.getReturns().stream()
+ .anyMatch(
+ item -> diffList.stream()
+ .anyMatch(
+ diff -> item.equals(diff.getNewQualifiedName())
+ )
+ );
+
+ if (matchArgs || matchReturns) {
+ FileDiff fileDiff = new FileDiff();
+
+ fileDiff.setChangeType(FileDiff.ChangeType.RELATED);
+ fileDiff.setNewQualifiedName(clazz);
+ fileDiff.setEntryPoint(true);
+ diffList.add(fileDiff);
+ }
+ });
+ });
+
+ // check whether the others are entry point
+ diffList.stream()
+ .filter(item -> !item.isEntryPoint())
+ .forEach(item -> {
+ boolean isEntry = isEntryPoint.test(item.getNewQualifiedName());
+ item.setEntryPoint(isEntry);
+ });
+ }
+
+ public void rebuildDependencyTree(List apiList) {
+ List dependencyTree = buildDependencyTree(apiList);
+ writeDependencyTree(dependencyTree);
+ }
+
+ private List buildDependencyTree(List apiList) {
+ if (CollectionUtil.isEmpty(apiList)) {
+ return Collections.emptyList();
+ }
+
+ List dependencyTree = new ArrayList<>(apiList.size());
+
+ for (T apiDoc : apiList) {
+ String docClass = apiDoc.getDocClass();
+ List docMethods = apiDoc.getMethods();
+ List apiInfoList = new ArrayList<>(docMethods.size());
+
+ // Get the derived classes which really used in api doc
+ List derivedClazz = docMethods.stream()
+ .map(IMethod::getDeclaringClass).filter(Objects::nonNull)
+ .map(JavaClass::getFullyQualifiedName)
+ .distinct().collect(Collectors.toList());
+
+ ApiDependency apiDependency = new ApiDependency(docClass, derivedClazz, apiInfoList);
+ dependencyTree.add(apiDependency);
+
+ for (IMethod docMethod : docMethods) {
+ String methodName = docMethod.getMethodName();
+ List argsClasses = docMethod.getArgsClasses();
+ List returnClasses = docMethod.getReturnClasses();
+ ApiDependency.ApiInfo apiInfo = new ApiDependency.ApiInfo(methodName, argsClasses, returnClasses);
+
+ apiInfoList.add(apiInfo);
+ }
+ }
+
+ return dependencyTree;
+ }
+
+}
diff --git a/src/main/java/com/ly/doc/helper/GitHelper.java b/src/main/java/com/ly/doc/helper/GitHelper.java
new file mode 100644
index 00000000..0ef806e3
--- /dev/null
+++ b/src/main/java/com/ly/doc/helper/GitHelper.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018-2023 smart-doc
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 com.ly.doc.helper;
+
+import com.power.common.util.StringUtil;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.diff.DiffEntry;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A git util build on JGit
+ *
+ * @author Fio
+ */
+public class GitHelper {
+
+ private Repository repository;
+
+ private GitHelper() {}
+
+ public static GitHelper create() {
+ GitHelper helper = new GitHelper();
+ helper.repository = helper.findRepo();
+ return helper;
+ }
+
+ public List getDiff(String commitId) {
+ if (StringUtil.isEmpty(commitId) || notGitRepo()) {
+ return Collections.emptyList();
+ }
+
+ try (Git git = new Git(repository)) {
+ ObjectId commitObjectId = repository.resolve(commitId);
+ RevCommit commit = new RevWalk(repository).parseCommit(commitObjectId);
+
+ ObjectId treeId = commit.getTree().getId();
+ CanonicalTreeParser oldTreeIter = new CanonicalTreeParser();
+ try (ObjectReader reader = repository.newObjectReader()) {
+ oldTreeIter.reset(reader, treeId);
+ }
+
+ ObjectId currentTreeId = repository.resolve("HEAD^{tree}");
+ CanonicalTreeParser newTreeIter = new CanonicalTreeParser();
+ try (ObjectReader reader = repository.newObjectReader()) {
+ newTreeIter.reset(reader, currentTreeId);
+ }
+
+ return git.diff()
+ .setNewTree(newTreeIter)
+ .setOldTree(oldTreeIter)
+ .call();
+ } catch (IOException | GitAPIException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Set getUncommitted() {
+ if (notGitRepo()) {
+ return Collections.emptySet();
+ }
+
+ try (Git git = new Git(repository)) {
+ return git.status().call().getUncommittedChanges();
+ } catch (GitAPIException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Set getUntracked() {
+ if (notGitRepo()) {
+ return Collections.emptySet();
+ }
+
+ try (Git git = new Git(repository)) {
+ return git.status().call().getUntracked();
+ } catch (GitAPIException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String getLatestCommitId() {
+ if (notGitRepo()) {
+ return "";
+ }
+
+ try {
+ ObjectId objectId = repository.resolve("HEAD");
+ return objectId.getName();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Check git repository, if not exist or io exception, return null
+ */
+ private Repository findRepo() {
+ try {
+ return new FileRepositoryBuilder()
+ .readEnvironment()
+ .findGitDir()
+ .build();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public boolean notGitRepo() {
+ return repository == null;
+ }
+
+ public String getWorkDir() {
+ return repository.getWorkTree().getAbsolutePath();
+ }
+
+}
diff --git a/src/main/java/com/ly/doc/model/ApiConfig.java b/src/main/java/com/ly/doc/model/ApiConfig.java
index 53cefbc6..a6d2da89 100644
--- a/src/main/java/com/ly/doc/model/ApiConfig.java
+++ b/src/main/java/com/ly/doc/model/ApiConfig.java
@@ -378,6 +378,16 @@ public static ApiConfig getInstance() {
*/
private String componentType;
+ /**
+ * whether to build the doc incrementally
+ */
+ private boolean increment;
+
+ /**
+ * the doc module absolute path, used for dependency tree file
+ */
+ private String baseDir;
+
public String getComponentType() {
return componentType;
}
@@ -995,4 +1005,20 @@ public ApiConfig setJarSourcePaths(List jarSourcePaths) {
public void setJarSourcePaths(SourceCodePath... jarSourcePaths) {
this.jarSourcePaths = CollectionUtil.asList(jarSourcePaths);
}
+
+ public boolean isIncrement() {
+ return increment;
+ }
+
+ public void setIncrement(boolean increment) {
+ this.increment = increment;
+ }
+
+ public String getBaseDir() {
+ return baseDir;
+ }
+
+ public void setBaseDir(String baseDir) {
+ this.baseDir = baseDir;
+ }
}
diff --git a/src/main/java/com/ly/doc/model/ApiDoc.java b/src/main/java/com/ly/doc/model/ApiDoc.java
index fe594d92..db39f7ac 100644
--- a/src/main/java/com/ly/doc/model/ApiDoc.java
+++ b/src/main/java/com/ly/doc/model/ApiDoc.java
@@ -23,9 +23,10 @@
import java.util.*;
+import com.power.common.util.CollectionUtil;
import com.power.common.util.StringUtil;
-public class ApiDoc implements Comparable {
+public class ApiDoc implements IDoc, Comparable {
/**
* Order of controller
@@ -245,4 +246,19 @@ public String toString() {
sb.append('}');
return sb.toString();
}
+
+ @Override
+ public String getDocClass() {
+ return this.packageName + "." + this.name;
+ }
+
+ @Override
+ public List getMethods() {
+ if (CollectionUtil.isEmpty(this.list)) {
+ return Collections.emptyList();
+ }
+
+ return new ArrayList<>(this.list);
+ }
+
}
diff --git a/src/main/java/com/ly/doc/model/ApiMethodDoc.java b/src/main/java/com/ly/doc/model/ApiMethodDoc.java
index 7e50914c..018af3e9 100644
--- a/src/main/java/com/ly/doc/model/ApiMethodDoc.java
+++ b/src/main/java/com/ly/doc/model/ApiMethodDoc.java
@@ -23,14 +23,16 @@
import java.io.Serializable;
import java.util.*;
+import com.ly.doc.utils.ParamUtil;
import com.power.common.util.StringUtil;
import com.ly.doc.constants.DocGlobalConstants;
import com.ly.doc.model.request.ApiRequestExample;
+import com.thoughtworks.qdox.model.JavaClass;
/**
* java api method info model.
*/
-public class ApiMethodDoc implements Serializable, Cloneable {
+public class ApiMethodDoc implements IMethod, Serializable, Cloneable {
private static final long serialVersionUID = 7211922919532562867L;
@@ -549,4 +551,27 @@ public ApiMethodDoc clone() {
throw new RuntimeException("clone apiMethodDoc is error", e);
}
}
+
+ @Override
+ public JavaClass getDeclaringClass() {
+ return null;
+ }
+
+ @Override
+ public String getMethodName() {
+ return this.name;
+ }
+
+ @Override
+ public List getArgsClasses() {
+ ArrayList paramList = new ArrayList<>(this.pathParams);
+ paramList.addAll(this.requestParams);
+ paramList.addAll(this.queryParams);
+ return ParamUtil.extractQualifiedName(paramList);
+ }
+
+ @Override
+ public List getReturnClasses() {
+ return ParamUtil.extractQualifiedName(this.responseParams);
+ }
}
diff --git a/src/main/java/com/ly/doc/model/IDoc.java b/src/main/java/com/ly/doc/model/IDoc.java
new file mode 100644
index 00000000..173dfb17
--- /dev/null
+++ b/src/main/java/com/ly/doc/model/IDoc.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018-2023 smart-doc
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 com.ly.doc.model;
+
+import java.util.List;
+
+/**
+ * @author Fio
+ */
+public interface IDoc {
+
+ /**
+ * doc class qualified name
+ */
+ String getDocClass();
+
+ /**
+ *
+ */
+ List getMethods();
+
+}
diff --git a/src/main/java/com/ly/doc/model/IMethod.java b/src/main/java/com/ly/doc/model/IMethod.java
new file mode 100644
index 00000000..7ee6c51e
--- /dev/null
+++ b/src/main/java/com/ly/doc/model/IMethod.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018-2023 smart-doc
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 com.ly.doc.model;
+
+import com.thoughtworks.qdox.model.JavaClass;
+
+import java.util.List;
+
+/**
+ * @author Fio
+ */
+public interface IMethod {
+
+ JavaClass getDeclaringClass();
+
+ String getMethodName();
+
+ List getArgsClasses();
+
+ List getReturnClasses();
+
+}
diff --git a/src/main/java/com/ly/doc/model/RpcJavaMethod.java b/src/main/java/com/ly/doc/model/RpcJavaMethod.java
index ff6c4011..f87f12e0 100644
--- a/src/main/java/com/ly/doc/model/RpcJavaMethod.java
+++ b/src/main/java/com/ly/doc/model/RpcJavaMethod.java
@@ -20,6 +20,8 @@
*/
package com.ly.doc.model;
+import com.ly.doc.utils.ParamUtil;
+import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.JavaType;
@@ -30,7 +32,7 @@
* for rpc
* @author yu 2020/1/29.
*/
-public class RpcJavaMethod {
+public class RpcJavaMethod implements IMethod {
/**
* java method
@@ -255,4 +257,24 @@ public RpcJavaMethod setActualTypesMap(Map actualTypesMap) {
this.actualTypesMap = actualTypesMap;
return this;
}
+
+ @Override
+ public JavaClass getDeclaringClass() {
+ return this.javaMethod.getDeclaringClass();
+ }
+
+ @Override
+ public String getMethodName() {
+ return this.name;
+ }
+
+ @Override
+ public List getArgsClasses() {
+ return ParamUtil.extractQualifiedName(this.requestParams);
+ }
+
+ @Override
+ public List getReturnClasses() {
+ return ParamUtil.extractQualifiedName(this.responseParams);
+ }
}
diff --git a/src/main/java/com/ly/doc/model/dependency/ApiDependency.java b/src/main/java/com/ly/doc/model/dependency/ApiDependency.java
new file mode 100644
index 00000000..d00b6213
--- /dev/null
+++ b/src/main/java/com/ly/doc/model/dependency/ApiDependency.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018-2023 smart-doc
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 com.ly.doc.model.dependency;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.List;
+
+/**
+ * @author Fio
+ */
+public class ApiDependency {
+
+ /**
+ * The endpoint class's full qualified name
+ */
+ private String clazz;
+
+ /**
+ * Derived class full qualified name if the entry point class is interface
+ */
+ private List derivedClazz;
+
+ /**
+ * Api methods in the entry point class
+ */
+ private List apis;
+
+ public ApiDependency() {
+ }
+
+ public ApiDependency(String clazz, List derivedClazz, List apis) {
+ this.clazz = clazz;
+ this.derivedClazz = derivedClazz;
+ this.apis = apis;
+ }
+
+ /**
+ * Api method simple info
+ */
+ public static class ApiInfo {
+
+ /**
+ * Api method name
+ */
+ private String method;
+
+ /**
+ * Api method args
+ */
+ private List args;
+
+ /**
+ * Api method return,include the generics
+ */
+ private List returns;
+
+ public ApiInfo() {
+ }
+
+ public ApiInfo(String method, List args, List returns) {
+ this.method = method;
+ this.args = args;
+ this.returns = returns;
+ }
+
+ public String getMethod() {
+ return method;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ public List getArgs() {
+ return args;
+ }
+
+ public void setArgs(List args) {
+ this.args = args;
+ }
+
+ public List getReturns() {
+ return returns;
+ }
+
+ public void setReturns(List returns) {
+ this.returns = returns;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ApiInfo)) return false;
+ ApiInfo apiInfo = (ApiInfo) o;
+ return Objects.equals(method, apiInfo.method) && Objects.equals(args, apiInfo.args) && Objects.equals(returns, apiInfo.returns);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, args, returns);
+ }
+ }
+
+ public String getClazz() {
+ return clazz;
+ }
+
+ public void setClazz(String clazz) {
+ this.clazz = clazz;
+ }
+
+ public List getDerivedClazz() {
+ return derivedClazz;
+ }
+
+ public void setDerivedClazz(List derivedClazz) {
+ this.derivedClazz = derivedClazz;
+ }
+
+ public List getApis() {
+ return apis;
+ }
+
+ public void setApis(List apis) {
+ this.apis = apis;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ApiDependency)) return false;
+ ApiDependency that = (ApiDependency) o;
+ return Objects.equals(clazz, that.clazz) && Objects.equals(derivedClazz, that.derivedClazz) && Objects.equals(apis, that.apis);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(clazz, derivedClazz, apis);
+ }
+}
diff --git a/src/main/java/com/ly/doc/model/dependency/DependencyTree.java b/src/main/java/com/ly/doc/model/dependency/DependencyTree.java
new file mode 100644
index 00000000..1d2cdc14
--- /dev/null
+++ b/src/main/java/com/ly/doc/model/dependency/DependencyTree.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2018-2023 smart-doc
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 com.ly.doc.model.dependency;
+
+import com.ly.doc.utils.JsonUtil;
+import com.power.common.util.FileUtil;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author Fio
+ */
+public class DependencyTree {
+
+ private static final String CONFIG_NAME = ".smart-doc-dependency.json";
+
+ /**
+ * dependency tree file
+ */
+ private transient File configFile;
+
+ /**
+ * The schema version for dependency tree format.
+ * Maybe you can rewrite the dependency-tree file when the format is incompatible.
+ */
+ private String schema = "v1";
+
+ /**
+ * Git commit id when smart-doc build.
+ */
+ private String commitId;
+
+ /**
+ * Api methods dependency trees.
+ */
+ private List dependencyTree;
+
+ private DependencyTree() {
+
+ }
+
+ /**
+ * Create or Load dependency tree config.
+ *
+ * @param baseDir the dependency tree config file base directory
+ * @return DependencyTree
+ */
+ public static DependencyTree detect(String baseDir) {
+ DependencyTree dependencyTree;
+
+ File configFile = new File(baseDir + File.separator + CONFIG_NAME);
+ if (!configFile.exists()) {
+ dependencyTree = Support.create(configFile);
+ } else {
+ dependencyTree = Support.load(configFile);
+ }
+
+ return dependencyTree;
+ }
+
+ public static void write(DependencyTree dependencyTree) {
+ // do something when the dependency-tree format is changed dependent on the version
+ Support.writeFile(dependencyTree);
+ }
+
+ public String getSchema() {
+ return schema;
+ }
+
+ public File getConfigFile() {
+ return configFile;
+ }
+
+ public String getCommitId() {
+ return commitId;
+ }
+
+ public List getDependencyTree() {
+ return dependencyTree;
+ }
+
+ public void setConfig(String commitId, List dependencyTree) {
+ this.commitId = commitId;
+ this.dependencyTree = dependencyTree;
+ }
+
+ /**
+ * Provide some utility methods
+ */
+ private static class Support {
+
+ private static DependencyTree create(File configFile) {
+ if (!configFile.exists()) {
+ try {
+ Files.createFile(configFile.toPath());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ DependencyTree instance = new DependencyTree();
+ instance.configFile = configFile;
+ instance.setConfig("", Collections.emptyList());
+
+ writeFile(instance);
+
+ return instance;
+ }
+
+ private static DependencyTree load(File configFile) {
+ if (configFile == null || !configFile.exists()) {
+ return null;
+ }
+
+ String content = readFile(configFile);
+ DependencyTree instance = JsonUtil.toObject(content, DependencyTree.class);
+ instance.configFile = configFile;
+ return instance;
+ }
+
+ private static void writeFile(DependencyTree instance) {
+ List distinctDependency = instance.getDependencyTree()
+ .stream().distinct().collect(Collectors.toList());
+ instance.setConfig(instance.getCommitId(), distinctDependency);
+ String content = JsonUtil.toPrettyJson(instance);
+ FileUtil.writeFileNotAppend(content, instance.configFile.getAbsolutePath());
+ }
+
+ private static String readFile(File configFile) {
+ try {
+ BufferedReader reader = new BufferedReader(new FileReader(configFile));
+ StringBuilder builder = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ builder.append(line);
+ }
+ return builder.toString();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/ly/doc/model/dependency/FileDiff.java b/src/main/java/com/ly/doc/model/dependency/FileDiff.java
new file mode 100644
index 00000000..889fbefd
--- /dev/null
+++ b/src/main/java/com/ly/doc/model/dependency/FileDiff.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018-2023 smart-doc
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 com.ly.doc.model.dependency;
+
+import java.util.Objects;
+
+/**
+ * Mark the file change type
+ *
+ * @author Fio
+ */
+public class FileDiff {
+
+ /**
+ * file change type
+ */
+ private ChangeType changeType;
+
+ /**
+ * old absolute path
+ */
+ private String oldQualifiedName;
+
+ /**
+ * new absolute path
+ */
+ private String newQualifiedName;
+
+ /**
+ * whether the class is entry point before build
+ */
+ private boolean isEntryPoint = false;
+
+ public enum ChangeType {
+ /** Add a new file to the project */
+ ADD,
+
+ /** Modify an existing file in the project (content and/or mode) */
+ MODIFY,
+
+ /** Delete an existing file from the project */
+ DELETE,
+
+ /** Rename an existing file to a new location */
+ RENAME,
+
+ /** Copy an existing file to a new location, keeping the original */
+ COPY,
+
+ /**
+ * File uncommitted, only with newPackagePath
+ */
+ UNCOMMITTED,
+
+ /**
+ * File untracked, only with newPackagePath
+ */
+ UNTRACKED,
+
+ /**
+ * The class related, only with newPackagePath
+ */
+ RELATED;
+
+ }
+
+ public ChangeType getChangeType() {
+ return changeType;
+ }
+
+ public void setChangeType(ChangeType changeType) {
+ this.changeType = changeType;
+ }
+
+ public String getOldQualifiedName() {
+ return oldQualifiedName;
+ }
+
+ public void setOldQualifiedName(String oldQualifiedName) {
+ this.oldQualifiedName = oldQualifiedName;
+ }
+
+ public String getNewQualifiedName() {
+ return newQualifiedName;
+ }
+
+ public void setNewQualifiedName(String newQualifiedName) {
+ this.newQualifiedName = newQualifiedName;
+ }
+
+ public boolean isEntryPoint() {
+ return isEntryPoint;
+ }
+
+ public void setEntryPoint(boolean entryPoint) {
+ isEntryPoint = entryPoint;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof FileDiff)) return false;
+ FileDiff fileDiff = (FileDiff) o;
+ return isEntryPoint == fileDiff.isEntryPoint && changeType == fileDiff.changeType && Objects.equals(oldQualifiedName, fileDiff.oldQualifiedName) && Objects.equals(newQualifiedName, fileDiff.newQualifiedName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(changeType, oldQualifiedName, newQualifiedName, isEntryPoint);
+ }
+}
diff --git a/src/main/java/com/ly/doc/model/rpc/RpcApiDoc.java b/src/main/java/com/ly/doc/model/rpc/RpcApiDoc.java
index 56a2da6f..0b151c2d 100644
--- a/src/main/java/com/ly/doc/model/rpc/RpcApiDoc.java
+++ b/src/main/java/com/ly/doc/model/rpc/RpcApiDoc.java
@@ -20,15 +20,20 @@
*/
package com.ly.doc.model.rpc;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import com.ly.doc.model.IDoc;
+import com.ly.doc.model.IMethod;
import com.ly.doc.model.RpcJavaMethod;
+import com.power.common.util.CollectionUtil;
/**
* @author yu 2020/5/16.
*/
-public class RpcApiDoc implements Comparable {
+public class RpcApiDoc implements IDoc, Comparable {
/**
* Order of controller
@@ -197,4 +202,17 @@ public int compareTo(RpcApiDoc o) {
}
return name.compareTo(o.getName());
}
+
+ @Override
+ public String getDocClass() {
+ return this.name;
+ }
+
+ @Override
+ public List getMethods() {
+ if (CollectionUtil.isEmpty(this.list)) {
+ return Collections.emptyList();
+ }
+ return new ArrayList<>(this.list);
+ }
}
diff --git a/src/main/java/com/ly/doc/template/IDocBuildTemplate.java b/src/main/java/com/ly/doc/template/IDocBuildTemplate.java
index c4287a49..7c58a4e8 100644
--- a/src/main/java/com/ly/doc/template/IDocBuildTemplate.java
+++ b/src/main/java/com/ly/doc/template/IDocBuildTemplate.java
@@ -20,34 +20,45 @@
*/
package com.ly.doc.template;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Collectors;
-
-import com.ly.doc.model.ApiConfig;
-import com.ly.doc.model.ApiDoc;
-import com.ly.doc.model.ApiGroup;
-import com.ly.doc.model.ApiMethodDoc;
-import com.ly.doc.utils.DocUtil;
-import com.power.common.util.CollectionUtil;
-import com.power.common.util.StringUtil;
import com.ly.doc.builder.ProjectDocConfigBuilder;
import com.ly.doc.constants.TornaConstants;
+import com.ly.doc.helper.DocBuildHelper;
+import com.ly.doc.model.dependency.FileDiff;
+import com.ly.doc.model.*;
import com.ly.doc.model.annotation.FrameworkAnnotations;
import com.ly.doc.utils.DocPathUtil;
+import com.ly.doc.utils.DocUtil;
+import com.power.common.util.CollectionUtil;
+import com.power.common.util.StringUtil;
+import com.thoughtworks.qdox.JavaProjectBuilder;
+import com.thoughtworks.qdox.model.JavaClass;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* @author yu 2019/12/21.
*/
-public interface IDocBuildTemplate {
+public interface IDocBuildTemplate {
- List getApiData(ProjectDocConfigBuilder projectBuilder);
+ default List getApiData(ProjectDocConfigBuilder projectBuilder) {
+ DocBuildHelper docBuildHelper = DocBuildHelper.create(projectBuilder);
- FrameworkAnnotations registeredAnnotations();
+ preRender(docBuildHelper);
+
+ Collection candidateClasses = getCandidateClasses(projectBuilder, docBuildHelper);
+ List apiList = renderApi(projectBuilder, candidateClasses);
+
+ postRender(docBuildHelper, apiList);
+
+ return apiList;
+ }
+
+ List renderApi(ProjectDocConfigBuilder projectBuilder, Collection candidateClasses);
+ FrameworkAnnotations registeredAnnotations();
/**
* handle group api docs
@@ -93,8 +104,8 @@ default List handleApiGroup(List apiDocList, ApiConfig apiConfig
continue;
}
List methodDocs = doc.getList().stream()
- .filter(l -> DocPathUtil.matches(l.getPath(), group.getPaths(), null))
- .collect(Collectors.toList());
+ .filter(l -> DocPathUtil.matches(l.getPath(), group.getPaths(), null))
+ .collect(Collectors.toList());
doc.setList(methodDocs);
}
}
@@ -113,4 +124,77 @@ default List handleApiGroup(List apiDocList, ApiConfig apiConfig
finalApiDocs.forEach(group -> group.setOrder(order.getAndIncrement()));
return finalApiDocs;
}
+
+ /**
+ * If build doc incrementally, we will filter the classes changed
+ * from the commit-id in increment-config-file.
+ * If not, we will return all classes.
+ *
+ * @param docBuilder docBuilder
+ * @param docBuildHelper incrementHelper
+ * @return the candidate classes
+ */
+ default Collection getCandidateClasses(ProjectDocConfigBuilder docBuilder, DocBuildHelper docBuildHelper) {
+ ApiConfig apiConfig = docBuilder.getApiConfig();
+ JavaProjectBuilder javaProjectBuilder = docBuilder.getJavaProjectBuilder();
+
+ if (!apiConfig.isIncrement()) {
+ return javaProjectBuilder.getClasses();
+ }
+
+ if (StringUtil.isEmpty(docBuildHelper.getDependencyTree().getCommitId())) {
+ // There is no commit-id, which means the user haven't built the whole project.
+ // We need to build the whole project this time,
+ // and record the latest commit-id and the newest api dependency tree.
+ return javaProjectBuilder.getClasses();
+ }
+
+ Set fileDiffList = docBuildHelper.getChangedFilesFromVCS(new Predicate() {
+ @Override
+ public boolean test(String s) {
+ return isEntryPoint(javaProjectBuilder, s);
+ }
+ });
+ if (CollectionUtil.isEmpty(fileDiffList)) {
+ return Collections.emptyList();
+ }
+
+ Collection result = new ArrayList<>(fileDiffList.size());
+ fileDiffList.forEach(item -> {
+ try {
+ JavaClass javaClass = javaProjectBuilder.getClassByName(item.getNewQualifiedName());
+ result.add(javaClass);
+ } catch (Exception ignore) {}
+ });
+
+ return result;
+ }
+
+ default void preRender(DocBuildHelper docBuildHelper) {
+
+ }
+
+ default void postRender(DocBuildHelper docBuildHelper, List apiList) {
+ docBuildHelper.rebuildDependencyTree(apiList);
+ }
+
+ default boolean isEntryPoint(JavaProjectBuilder javaProjectBuilder, String javaClassName) {
+ if (StringUtil.isEmpty(javaClassName)) {
+ return false;
+ }
+
+ JavaClass javaClass = null;
+ try {
+ javaClass = javaProjectBuilder.getClassByName(javaClassName);
+ } catch (Exception ignore) {}
+
+ if (javaClass == null) {
+ return false;
+ }
+
+ return isEntryPoint(javaClass, registeredAnnotations());
+ }
+
+ boolean isEntryPoint(JavaClass javaClass, FrameworkAnnotations frameworkAnnotations);
+
}
diff --git a/src/main/java/com/ly/doc/template/IRestDocTemplate.java b/src/main/java/com/ly/doc/template/IRestDocTemplate.java
index 8cd6f745..f510a88c 100644
--- a/src/main/java/com/ly/doc/template/IRestDocTemplate.java
+++ b/src/main/java/com/ly/doc/template/IRestDocTemplate.java
@@ -57,14 +57,14 @@ public interface IRestDocTemplate extends IBaseDocBuildTemplate {
AtomicInteger atomicInteger = new AtomicInteger(1);
default List processApiData(ProjectDocConfigBuilder projectBuilder, FrameworkAnnotations frameworkAnnotations,
- List configApiReqParams, IRequestMappingHandler baseMappingHandler, IHeaderHandler headerHandler) {
+ List configApiReqParams, IRequestMappingHandler baseMappingHandler, IHeaderHandler headerHandler,
+ Collection javaClasses) {
ApiConfig apiConfig = projectBuilder.getApiConfig();
List apiDocList = new ArrayList<>();
int order = 0;
boolean setCustomOrder = false;
- Collection classes = projectBuilder.getJavaProjectBuilder().getClasses();
// exclude class is ignore
- for (JavaClass cls : classes) {
+ for (JavaClass cls : javaClasses) {
if (StringUtil.isNotEmpty(apiConfig.getPackageFilters())) {
// from smart config
if (!DocUtil.isMatch(apiConfig.getPackageFilters(), cls)) {
@@ -73,7 +73,7 @@ default List processApiData(ProjectDocConfigBuilder projectBuilder, Fram
}
// from tag
DocletTag ignoreTag = cls.getTagByName(DocTags.IGNORE);
- if (!defaultEntryPoint(cls, frameworkAnnotations) || Objects.nonNull(ignoreTag)) {
+ if (!isEntryPoint(cls, frameworkAnnotations) || Objects.nonNull(ignoreTag)) {
continue;
}
String strOrder = JavaClassUtil.getClassTagsValue(cls, DocTags.ORDER, Boolean.TRUE);
@@ -1068,16 +1068,11 @@ default boolean defaultEntryPoint(JavaClass cls, FrameworkAnnotations frameworkA
}
List classAnnotations = DocClassUtil.getAnnotations(cls);
Map entryAnnotationMap = frameworkAnnotations.getEntryAnnotations();
- for (JavaAnnotation annotation : classAnnotations) {
+
+ return classAnnotations.stream().anyMatch(annotation -> {
String name = annotation.getType().getValue();
- if (entryAnnotationMap.containsKey(name)) {
- return true;
- }
- if (isEntryPoint(cls, frameworkAnnotations)) {
- return true;
- }
- }
- return false;
+ return entryAnnotationMap.containsKey(name);
+ });
}
default List getParentsClassMethods(ApiConfig apiConfig, ProjectDocConfigBuilder projectBuilder, JavaClass cls) {
diff --git a/src/main/java/com/ly/doc/template/JaxrsDocBuildTemplate.java b/src/main/java/com/ly/doc/template/JaxrsDocBuildTemplate.java
index 92302b8a..1ddcf3f7 100644
--- a/src/main/java/com/ly/doc/template/JaxrsDocBuildTemplate.java
+++ b/src/main/java/com/ly/doc/template/JaxrsDocBuildTemplate.java
@@ -68,16 +68,15 @@ public class JaxrsDocBuildTemplate implements IDocBuildTemplate, IRestDo
@Override
- public List getApiData(ProjectDocConfigBuilder projectBuilder) {
+ public List renderApi(ProjectDocConfigBuilder projectBuilder, Collection candidateClasses) {
ApiConfig apiConfig = projectBuilder.getApiConfig();
FrameworkAnnotations frameworkAnnotations = registeredAnnotations();
this.headers = apiConfig.getRequestHeaders();
List apiDocList = new ArrayList<>();
int order = 0;
- Collection classes = projectBuilder.getJavaProjectBuilder().getClasses();
boolean setCustomOrder = false;
// exclude class is ignore
- for (JavaClass cls : classes) {
+ for (JavaClass cls : candidateClasses) {
if (StringUtil.isNotEmpty(apiConfig.getPackageFilters())) {
// from smart config
if (!DocUtil.isMatch(apiConfig.getPackageFilters(), cls)) {
@@ -283,6 +282,11 @@ public FrameworkAnnotations registeredAnnotations() {
@Override
public boolean isEntryPoint(JavaClass cls, FrameworkAnnotations frameworkAnnotations) {
+ boolean isDefaultEntryPoint = defaultEntryPoint(cls, frameworkAnnotations);
+ if (isDefaultEntryPoint) {
+ return true;
+ }
+
if (cls.isAnnotation() || cls.isEnum()) {
return false;
}
diff --git a/src/main/java/com/ly/doc/template/RpcDocBuildTemplate.java b/src/main/java/com/ly/doc/template/RpcDocBuildTemplate.java
index 4a50c186..b49a8ce1 100644
--- a/src/main/java/com/ly/doc/template/RpcDocBuildTemplate.java
+++ b/src/main/java/com/ly/doc/template/RpcDocBuildTemplate.java
@@ -53,12 +53,12 @@ public class RpcDocBuildTemplate implements IDocBuildTemplate, IRpcDo
private final AtomicInteger atomicInteger = new AtomicInteger(1);
@Override
- public List getApiData(ProjectDocConfigBuilder projectBuilder) {
+ public List renderApi(ProjectDocConfigBuilder projectBuilder, Collection candidateClasses) {
ApiConfig apiConfig = projectBuilder.getApiConfig();
List apiDocList = new ArrayList<>();
int order = 0;
boolean setCustomOrder = false;
- for (JavaClass cls : projectBuilder.getJavaProjectBuilder().getClasses()) {
+ for (JavaClass cls : candidateClasses) {
if (StringUtil.isNotEmpty(apiConfig.getPackageFilters())) {
// check package
if (!DocUtil.isMatch(apiConfig.getPackageFilters(), cls)) {
diff --git a/src/main/java/com/ly/doc/template/SolonDocBuildTemplate.java b/src/main/java/com/ly/doc/template/SolonDocBuildTemplate.java
index 64754a8d..9d220e86 100644
--- a/src/main/java/com/ly/doc/template/SolonDocBuildTemplate.java
+++ b/src/main/java/com/ly/doc/template/SolonDocBuildTemplate.java
@@ -61,13 +61,13 @@
public class SolonDocBuildTemplate implements IDocBuildTemplate, IRestDocTemplate {
@Override
- public List getApiData(ProjectDocConfigBuilder projectBuilder) {
+ public List renderApi(ProjectDocConfigBuilder projectBuilder, Collection candidateClasses) {
ApiConfig apiConfig = projectBuilder.getApiConfig();
List configApiReqParams = Stream.of(apiConfig.getRequestHeaders(), apiConfig.getRequestParams()).filter(Objects::nonNull)
.flatMap(Collection::stream).collect(Collectors.toList());
FrameworkAnnotations frameworkAnnotations = registeredAnnotations();
List apiDocList = processApiData(projectBuilder, frameworkAnnotations, configApiReqParams,
- new SolonRequestMappingHandler(), new SolonRequestHeaderHandler());
+ new SolonRequestMappingHandler(), new SolonRequestHeaderHandler(), candidateClasses);
// sort
if (apiConfig.isSortByTitle()) {
Collections.sort(apiDocList);
@@ -83,6 +83,11 @@ public boolean ignoreReturnObject(String typeName, List ignoreParams) {
@Override
public boolean isEntryPoint(JavaClass cls, FrameworkAnnotations frameworkAnnotations) {
+ boolean isDefaultEntryPoint = defaultEntryPoint(cls, frameworkAnnotations);
+ if (isDefaultEntryPoint) {
+ return true;
+ }
+
for (JavaAnnotation annotation : cls.getAnnotations()) {
String name = annotation.getType().getValue();
if (SolonAnnotations.REMOTING.equals(name)) {
diff --git a/src/main/java/com/ly/doc/template/SpringBootDocBuildTemplate.java b/src/main/java/com/ly/doc/template/SpringBootDocBuildTemplate.java
index 294d83a6..9a48ab52 100644
--- a/src/main/java/com/ly/doc/template/SpringBootDocBuildTemplate.java
+++ b/src/main/java/com/ly/doc/template/SpringBootDocBuildTemplate.java
@@ -44,13 +44,13 @@
public class SpringBootDocBuildTemplate implements IDocBuildTemplate, IRestDocTemplate {
@Override
- public List getApiData(ProjectDocConfigBuilder projectBuilder) {
+ public List renderApi(ProjectDocConfigBuilder projectBuilder, Collection candidateClasses) {
ApiConfig apiConfig = projectBuilder.getApiConfig();
List configApiReqParams = Stream.of(apiConfig.getRequestHeaders(), apiConfig.getRequestParams()).filter(Objects::nonNull)
.flatMap(Collection::stream).collect(Collectors.toList());
FrameworkAnnotations frameworkAnnotations = registeredAnnotations();
List apiDocList = this.processApiData(projectBuilder, frameworkAnnotations,
- configApiReqParams, new SpringMVCRequestMappingHandler(), new SpringMVCRequestHeaderHandler());
+ configApiReqParams, new SpringMVCRequestMappingHandler(), new SpringMVCRequestHeaderHandler(), candidateClasses);
// sort
if (apiConfig.isSortByTitle()) {
Collections.sort(apiDocList);
@@ -182,6 +182,11 @@ public FrameworkAnnotations registeredAnnotations() {
@Override
public boolean isEntryPoint(JavaClass javaClass, FrameworkAnnotations frameworkAnnotations) {
+ boolean isDefaultEntryPoint = defaultEntryPoint(javaClass, frameworkAnnotations);
+ if (isDefaultEntryPoint) {
+ return true;
+ }
+
if (javaClass.isAnnotation() || javaClass.isEnum()) {
return false;
}
diff --git a/src/main/java/com/ly/doc/utils/DocClassUtil.java b/src/main/java/com/ly/doc/utils/DocClassUtil.java
index 33ca6b21..3f2de971 100644
--- a/src/main/java/com/ly/doc/utils/DocClassUtil.java
+++ b/src/main/java/com/ly/doc/utils/DocClassUtil.java
@@ -23,8 +23,6 @@
import java.lang.reflect.InvocationTargetException;
import java.util.*;
-import javax.annotation.Nonnull;
-
import com.ly.doc.model.ApiReturn;
import com.power.common.util.StringUtil;
import com.ly.doc.filter.ReturnTypeProcessor;
@@ -356,7 +354,7 @@ public static List getAnnotations(JavaClass cls) {
return classAnnotations;
}
- public static T newInstance(@Nonnull Class classWithNoArgsConstructor) {
+ public static T newInstance(Class classWithNoArgsConstructor) {
try {
return classWithNoArgsConstructor.getConstructor().newInstance();
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException |
diff --git a/src/main/java/com/ly/doc/utils/JsonUtil.java b/src/main/java/com/ly/doc/utils/JsonUtil.java
index 8a170095..9bd3d179 100644
--- a/src/main/java/com/ly/doc/utils/JsonUtil.java
+++ b/src/main/java/com/ly/doc/utils/JsonUtil.java
@@ -58,4 +58,8 @@ public static String toPrettyJson(Object src) {
Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
return gson.toJson(src);
}
+
+ public static T toObject(String json, Class clazz) {
+ return new Gson().fromJson(json, clazz);
+ }
}
diff --git a/src/main/java/com/ly/doc/utils/ParamUtil.java b/src/main/java/com/ly/doc/utils/ParamUtil.java
index 43f6283e..6230adde 100644
--- a/src/main/java/com/ly/doc/utils/ParamUtil.java
+++ b/src/main/java/com/ly/doc/utils/ParamUtil.java
@@ -1,12 +1,12 @@
package com.ly.doc.utils;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
import com.ly.doc.builder.ProjectDocConfigBuilder;
import com.ly.doc.constants.DocGlobalConstants;
import com.ly.doc.constants.DocTags;
import com.ly.doc.model.ApiParam;
+import com.power.common.util.CollectionUtil;
import com.power.common.util.StringUtil;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;
@@ -41,4 +41,28 @@ public static String formatMockValue(String mock) {
}
return mock.replaceAll("\\\\","");
}
+
+ public static List extractQualifiedName(List paramList) {
+ if (CollectionUtil.isEmpty(paramList)) {
+ return Collections.emptyList();
+ }
+
+ Set set = new HashSet<>();
+ for (ApiParam param : paramList) {
+ String className = param.getClassName();
+
+ if (StringUtil.isEmpty(className)) {
+ continue;
+ }
+
+ int index = className.indexOf("<");
+ if (index > -1) {
+ className = className.substring(0, index);
+ }
+
+ set.add(className);
+ }
+
+ return new ArrayList<>(set);
+ }
}
diff --git a/src/test/java/com/ly/doc/util/DocClassUtilTest.java b/src/test/java/com/ly/doc/util/DocClassUtilTest.java
index d4ef7e21..26cd0065 100644
--- a/src/test/java/com/ly/doc/util/DocClassUtilTest.java
+++ b/src/test/java/com/ly/doc/util/DocClassUtilTest.java
@@ -16,7 +16,7 @@ public class DocClassUtilTest {
@Test
public void testGetSimpleGicName() {
char me = 'k';
- String className = "com.power.doc.controller.Teacher,com.power.doc.controller.Teacher,com.power.doc.controller.Teacher>";
+ String className = "com.ly.doc.controller.Teacher,com.ly.doc.controller.Teacher,com.ly.doc.controller.Teacher>";
String[] arr = DocClassUtil.getSimpleGicName(className);
// System.out.println("arr:"+ JSON.toJSONString(arr));
}
diff --git a/src/test/java/com/ly/doc/util/ParamUtilTest.java b/src/test/java/com/ly/doc/util/ParamUtilTest.java
index 4ce79181..e69e1c39 100644
--- a/src/test/java/com/ly/doc/util/ParamUtilTest.java
+++ b/src/test/java/com/ly/doc/util/ParamUtilTest.java
@@ -1,12 +1,96 @@
package com.ly.doc.util;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.ly.doc.model.ApiParam;
import com.ly.doc.utils.ParamUtil;
import org.junit.jupiter.api.Test;
+import java.util.List;
+
public class ParamUtilTest {
@Test
public void testFormatMockValue() {
System.out.printf(ParamUtil.formatMockValue("*\\/5 * * *"));
}
+
+ @Test
+ public void testExtractQualifiedName() {
+ String paramJson = "[\n" +
+ " {\n" +
+ " \"className\": \"org.example.springboot.dto.PetDTO\",\n" +
+ " \"id\": 1,\n" +
+ " \"field\": \"name\",\n" +
+ " \"type\": \"string\",\n" +
+ " \"desc\": \"name\",\n" +
+ " \"required\": false,\n" +
+ " \"version\": \"-\",\n" +
+ " \"pid\": 0,\n" +
+ " \"pathParam\": false,\n" +
+ " \"queryParam\": false,\n" +
+ " \"value\": \"\",\n" +
+ " \"hasItems\": false,\n" +
+ " \"maxLength\": \"\",\n" +
+ " \"configParam\": false,\n" +
+ " \"selfReferenceLoop\": false\n" +
+ " },\n" +
+ " {\n" +
+ " \"className\": \"org.example.springboot.dto.PetDTO\",\n" +
+ " \"id\": 2,\n" +
+ " \"field\": \"age\",\n" +
+ " \"type\": \"int32\",\n" +
+ " \"desc\": \"age\",\n" +
+ " \"required\": false,\n" +
+ " \"version\": \"-\",\n" +
+ " \"pid\": 0,\n" +
+ " \"pathParam\": false,\n" +
+ " \"queryParam\": false,\n" +
+ " \"value\": \"0\",\n" +
+ " \"hasItems\": false,\n" +
+ " \"maxLength\": \"\",\n" +
+ " \"configParam\": false,\n" +
+ " \"selfReferenceLoop\": false\n" +
+ " },\n" +
+ " {\n" +
+ " \"className\": \"org.example.springboot.dto.PetDTO\",\n" +
+ " \"id\": 3,\n" +
+ " \"field\": \"master\",\n" +
+ " \"type\": \"object\",\n" +
+ " \"desc\": \"master\",\n" +
+ " \"required\": false,\n" +
+ " \"version\": \"-\",\n" +
+ " \"pid\": 0,\n" +
+ " \"pathParam\": false,\n" +
+ " \"queryParam\": false,\n" +
+ " \"value\": \"\",\n" +
+ " \"hasItems\": false,\n" +
+ " \"maxLength\": \"\",\n" +
+ " \"configParam\": false,\n" +
+ " \"selfReferenceLoop\": false\n" +
+ " },\n" +
+ " {\n" +
+ " \"className\": \"org.example.springboot.dto.UserDTO\",\n" +
+ " \"id\": 4,\n" +
+ " \"field\": \"└─username\",\n" +
+ " \"type\": \"string\",\n" +
+ " \"desc\": \"No comments found.\",\n" +
+ " \"required\": false,\n" +
+ " \"version\": \"-\",\n" +
+ " \"pid\": 3,\n" +
+ " \"pathParam\": false,\n" +
+ " \"queryParam\": false,\n" +
+ " \"value\": \"\",\n" +
+ " \"hasItems\": false,\n" +
+ " \"maxLength\": \"\",\n" +
+ " \"configParam\": false,\n" +
+ " \"selfReferenceLoop\": false\n" +
+ " }\n" +
+ "]";
+ List paramList = new Gson().fromJson(paramJson, new TypeToken>() {
+ });
+ List qualifiedList = ParamUtil.extractQualifiedName(paramList);
+ qualifiedList.forEach(System.out::println);
+ }
+
}