Skip to content
This repository was archived by the owner on Dec 4, 2025. It is now read-only.

Commit 5ed744d

Browse files
authored
Merge pull request #866 from linwumingshi/fix/issues/865
fix: 🐛 Fix the issue where the generated interface documentation lacks descriptive information when using `Dubbo RPC` or `@javadoc` tags and overriding a parent class or interface without Javadoc comments.
2 parents f3f11ac + 70d4820 commit 5ed744d

2 files changed

Lines changed: 141 additions & 54 deletions

File tree

src/main/java/com/ly/doc/template/IBaseDocBuildTemplate.java

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,19 @@
2020
*/
2121
package com.ly.doc.template;
2222

23-
import java.util.ArrayList;
24-
import java.util.Collections;
25-
import java.util.HashMap;
26-
import java.util.HashSet;
27-
import java.util.List;
28-
import java.util.Map;
29-
import java.util.Objects;
30-
import java.util.Set;
31-
32-
import com.ly.doc.model.*;
33-
import com.ly.doc.utils.*;
34-
import com.power.common.util.StringUtil;
35-
import com.power.common.util.UrlUtil;
3623
import com.ly.doc.builder.ProjectDocConfigBuilder;
3724
import com.ly.doc.constants.DocGlobalConstants;
3825
import com.ly.doc.constants.DocTags;
3926
import com.ly.doc.helper.ParamsBuildHelper;
27+
import com.ly.doc.model.*;
4028
import com.ly.doc.model.annotation.FrameworkAnnotations;
41-
import com.thoughtworks.qdox.model.DocletTag;
42-
import com.thoughtworks.qdox.model.JavaAnnotation;
43-
import com.thoughtworks.qdox.model.JavaMethod;
44-
import com.thoughtworks.qdox.model.JavaParameter;
45-
import com.thoughtworks.qdox.model.JavaType;
29+
import com.ly.doc.utils.*;
30+
import com.power.common.util.StringUtil;
31+
import com.thoughtworks.qdox.model.*;
32+
33+
import java.util.*;
4634

4735
import static com.ly.doc.constants.DocGlobalConstants.NO_COMMENTS_FOUND;
48-
import static com.ly.doc.constants.DocTags.*;
4936

5037
/**
5138
* @author yu3.sun on 2022/10/2
@@ -78,11 +65,7 @@ default List<ApiParam> buildReturnApiParams(DocJavaMethod docJavaMethod, Project
7865
&& Objects.isNull(method.getTagByName(DocTags.IGNORE_RESPONSE_BODY_ADVICE))) {
7966
String responseBodyAdvice = projectBuilder.getApiConfig().getResponseBodyAdvice().getClassName();
8067
if (!returnTypeGenericCanonicalName.startsWith(responseBodyAdvice)) {
81-
returnTypeGenericCanonicalName = new StringBuffer().append(responseBodyAdvice)
82-
.append("<")
83-
.append(returnTypeGenericCanonicalName)
84-
.append(">")
85-
.toString();
68+
returnTypeGenericCanonicalName = responseBodyAdvice + "<" + returnTypeGenericCanonicalName + ">";
8669
}
8770
}
8871
Map<String, JavaType> actualTypesMap = docJavaMethod.getActualTypesMap();
@@ -161,7 +144,7 @@ default List<DocJavaParameter> getJavaParameterList(ProjectDocConfigBuilder buil
161144
javaType = actualTypesMap.get(javaType.getCanonicalName());
162145
}
163146
apiJavaParameter.setTypeValue(javaType.getValue());
164-
String genericCanonicalName = javaType.getGenericCanonicalName();
147+
StringBuilder genericCanonicalName = new StringBuilder(javaType.getGenericCanonicalName());
165148
String fullyQualifiedName = javaType.getFullyQualifiedName();
166149
apiJavaParameter.setFullyQualifiedName(fullyQualifiedName);
167150
String genericFullyQualifiedName = javaType.getGenericFullyQualifiedName();
@@ -173,15 +156,15 @@ default List<DocJavaParameter> getJavaParameterList(ProjectDocConfigBuilder buil
173156
String rewriteClassName = getRewriteClassName(replacementMap, genericFullyQualifiedName, commentClass);
174157
// rewrite class
175158
if (JavaClassValidateUtil.isClassName(rewriteClassName)) {
176-
genericCanonicalName = rewriteClassName;
159+
genericCanonicalName = new StringBuilder(rewriteClassName);
177160
genericFullyQualifiedName = DocClassUtil.getSimpleName(rewriteClassName);
178161
}
179-
if (JavaClassValidateUtil.isMvcIgnoreParams(genericCanonicalName,
162+
if (JavaClassValidateUtil.isMvcIgnoreParams(genericCanonicalName.toString(),
180163
builder.getApiConfig().getIgnoreRequestParams())) {
181164
continue;
182165
}
183166
genericFullyQualifiedName = DocClassUtil.rewriteRequestParam(genericFullyQualifiedName);
184-
genericCanonicalName = DocClassUtil.rewriteRequestParam(genericCanonicalName);
167+
genericCanonicalName = new StringBuilder(DocClassUtil.rewriteRequestParam(genericCanonicalName.toString()));
185168
List<JavaAnnotation> annotations = parameter.getAnnotations();
186169
apiJavaParameter.setAnnotations(annotations);
187170
for (JavaAnnotation annotation : annotations) {
@@ -192,17 +175,17 @@ default List<DocJavaParameter> getJavaParameterList(ProjectDocConfigBuilder buil
192175
&& Objects.isNull(javaMethod.getTagByName(DocTags.IGNORE_REQUEST_BODY_ADVICE))) {
193176
String requestBodyAdvice = builder.getApiConfig().getRequestBodyAdvice().getClassName();
194177
genericFullyQualifiedName = requestBodyAdvice;
195-
genericCanonicalName = requestBodyAdvice + "<" + genericCanonicalName + ">";
178+
genericCanonicalName = new StringBuilder(requestBodyAdvice + "<" + genericCanonicalName + ">");
196179
}
197180
}
198181
}
199182
if (JavaClassValidateUtil.isCollection(genericFullyQualifiedName)
200183
|| JavaClassValidateUtil.isArray(genericFullyQualifiedName)) {
201-
if (JavaClassValidateUtil.isCollection(genericCanonicalName)) {
202-
genericCanonicalName = genericCanonicalName + "<T>";
184+
if (JavaClassValidateUtil.isCollection(genericCanonicalName.toString())) {
185+
genericCanonicalName.append("<T>");
203186
}
204187
}
205-
apiJavaParameter.setGenericCanonicalName(genericCanonicalName);
188+
apiJavaParameter.setGenericCanonicalName(genericCanonicalName.toString());
206189
apiJavaParameter.setGenericFullyQualifiedName(genericFullyQualifiedName);
207190
apiJavaParameterList.add(apiJavaParameter);
208191
}

src/main/java/com/ly/doc/template/IJavadocDocTemplate.java

Lines changed: 126 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,35 @@
3333

3434
import java.util.*;
3535
import java.util.concurrent.atomic.AtomicInteger;
36+
import java.util.function.Function;
37+
import java.util.stream.Collectors;
3638

3739
import static com.ly.doc.constants.DocTags.DEPRECATED;
3840
import static com.ly.doc.constants.DocTags.IGNORE;
3941

4042
public interface IJavadocDocTemplate extends IBaseDocBuildTemplate {
4143

44+
/**
45+
* Add method modifiers
46+
* @return boolean
47+
*/
4248
boolean addMethodModifiers();
4349

50+
/**
51+
* Convert JavaMethod to JavadocJavaMethod
52+
* @param apiConfig ApiConfig
53+
* @param method JavaMethod
54+
* @param actualTypesMap Map
55+
* @return JavadocJavaMethod
56+
*/
4457
default JavadocJavaMethod convertToJavadocJavaMethod(ApiConfig apiConfig, JavaMethod method,
4558
Map<String, JavaType> actualTypesMap) {
4659
JavaClass cls = method.getDeclaringClass();
4760
JavadocJavaMethod javadocJavaMethod = new JavadocJavaMethod();
4861
javadocJavaMethod.setJavaMethod(method);
4962
javadocJavaMethod.setName(method.getName());
5063
javadocJavaMethod.setActualTypesMap(actualTypesMap);
51-
String methodDefine = methodDefinition(method, actualTypesMap);
64+
String methodDefine = this.methodDefinition(method, actualTypesMap);
5265
String scapeMethod = methodDefine.replaceAll("<", "&lt;");
5366
scapeMethod = scapeMethod.replaceAll(">", "&gt;");
5467

@@ -83,9 +96,15 @@ default JavadocJavaMethod convertToJavadocJavaMethod(ApiConfig apiConfig, JavaMe
8396
return javadocJavaMethod;
8497
}
8598

99+
/**
100+
* Get method definition
101+
* @param method JavaMethod
102+
* @param actualTypesMap Map
103+
* @return String
104+
*/
86105
default String methodDefinition(JavaMethod method, Map<String, JavaType> actualTypesMap) {
87106
StringBuilder methodBuilder = new StringBuilder();
88-
if (addMethodModifiers()) {
107+
if (this.addMethodModifiers()) {
89108
// append method modifiers
90109
method.getModifiers().forEach(item -> methodBuilder.append(item).append(" "));
91110
}
@@ -102,33 +121,60 @@ default String methodDefinition(JavaMethod method, Map<String, JavaType> actualT
102121
return methodBuilder.toString();
103122
}
104123

124+
/**
125+
* Get parent class methods
126+
* @param apiConfig ApiConfig
127+
* @param cls JavaClass
128+
* @return List
129+
*/
105130
default List<? extends JavadocJavaMethod> getParentsClassMethods(ApiConfig apiConfig, JavaClass cls) {
106131
List<JavadocJavaMethod> docJavaMethods = new ArrayList<>();
107132
JavaClass parentClass = cls.getSuperJavaClass();
133+
// if parent class is not null and not Object
108134
if (Objects.nonNull(parentClass) && !JavaTypeConstants.OBJECT_SIMPLE_NAME.equals(parentClass.getSimpleName())) {
109135
Map<String, JavaType> actualTypesMap = JavaClassUtil.getActualTypesMap(parentClass);
110136
List<JavaMethod> parentMethodList = parentClass.getMethods();
111137
for (JavaMethod method : parentMethodList) {
112-
docJavaMethods.add(convertToJavadocJavaMethod(apiConfig, method, actualTypesMap));
138+
docJavaMethods.add(this.convertToJavadocJavaMethod(apiConfig, method, actualTypesMap));
113139
}
114-
docJavaMethods.addAll(getParentsClassMethods(apiConfig, parentClass));
140+
// add interface methods
141+
docJavaMethods.addAll(this.getInterfaceMethods(apiConfig, parentClass));
142+
// add parent class methods
143+
docJavaMethods.addAll(this.getParentsClassMethods(apiConfig, parentClass));
115144
}
116145
return docJavaMethods;
117146
}
118147

148+
/**
149+
* Get interface methods
150+
* @param apiConfig ApiConfig
151+
* @param cls JavaClass
152+
* @return List
153+
*/
119154
default List<? extends JavadocJavaMethod> getInterfaceMethods(ApiConfig apiConfig, JavaClass cls) {
120155
List<JavadocJavaMethod> docJavaMethods = new ArrayList<>();
121156
for (JavaClass javaInterface : cls.getInterfaces()) {
122157
Map<String, JavaType> actualTypesMap = JavaClassUtil.getActualTypesMap(javaInterface);
123158
List<JavaMethod> interfaceMethodList = javaInterface.getMethods();
124159
for (JavaMethod method : interfaceMethodList) {
125-
docJavaMethods.add(convertToJavadocJavaMethod(apiConfig, method, actualTypesMap));
160+
docJavaMethods.add(this.convertToJavadocJavaMethod(apiConfig, method, actualTypesMap));
126161
}
127-
docJavaMethods.addAll(getInterfaceMethods(apiConfig, javaInterface));
162+
// add interface methods
163+
docJavaMethods.addAll(this.getInterfaceMethods(apiConfig, javaInterface));
128164
}
129165
return docJavaMethods;
130166
}
131167

168+
/**
169+
* Constructs a list of request parameters.
170+
* @param javaMethod The JavaMethod object, used to extract method information.
171+
* @param builder The ProjectDocConfigBuilder object, containing project configuration
172+
* details.
173+
* @param atomicInteger An AtomicInteger, used to generate unique parameter IDs.
174+
* @param actualTypesMap A map of actual types, used for type replacement.
175+
* @return A List of ApiParam objects representing the request parameters or null if
176+
* no parameters exist.
177+
*/
132178
default List<ApiParam> requestParams(final JavaMethod javaMethod, ProjectDocConfigBuilder builder,
133179
AtomicInteger atomicInteger, Map<String, JavaType> actualTypesMap) {
134180
boolean isStrict = builder.getApiConfig().isStrict();
@@ -239,13 +285,25 @@ else if (javaClass.isEnum()) {
239285
return paramList;
240286
}
241287

288+
/**
289+
* Builds a list of service methods. This method parses the given Java class, extracts
290+
* methods that meet certain criteria, and generates corresponding JavadocJavaMethod
291+
* objects for them.
292+
* @param cls The Java class to parse.
293+
* @param apiConfig The API configuration object, containing rules for documentation
294+
* generation.
295+
* @param projectBuilder The project documentation configuration builder, used to
296+
* construct project-level documentation configurations.
297+
* @return A list containing documented methods represented as JavadocJavaMethod
298+
* objects.
299+
*/
242300
default List<? extends JavadocJavaMethod> buildServiceMethod(final JavaClass cls, ApiConfig apiConfig,
243301
ProjectDocConfigBuilder projectBuilder) {
244-
String clazName = cls.getCanonicalName();
302+
String clsCanonicalName = cls.getCanonicalName();
245303
List<JavaMethod> methods = cls.getMethods();
246304
List<JavadocJavaMethod> methodDocList = new ArrayList<>(methods.size());
247305

248-
Set<String> filterMethods = DocUtil.findFilterMethods(clazName);
306+
Set<String> filterMethods = DocUtil.findFilterMethods(clsCanonicalName);
249307
boolean needAllMethods = filterMethods.contains(DocGlobalConstants.DEFAULT_FILTER_METHOD);
250308

251309
for (JavaMethod method : methods) {
@@ -260,31 +318,51 @@ default List<? extends JavadocJavaMethod> buildServiceMethod(final JavaClass cls
260318
"Unable to find comment for method " + method.getName() + " in " + cls.getCanonicalName());
261319
}
262320
if (needAllMethods || filterMethods.contains(method.getName())) {
263-
JavadocJavaMethod apiMethodDoc = convertToJavadocJavaMethod(apiConfig, method, null);
321+
JavadocJavaMethod apiMethodDoc = this.convertToJavadocJavaMethod(apiConfig, method, null);
264322
methodDocList.add(apiMethodDoc);
265323
}
266324

267325
}
268-
// add parent class methods
269-
methodDocList.addAll(getParentsClassMethods(apiConfig, cls));
270-
if (cls.isInterface() || cls.isAbstract()) {
271-
methodDocList.addAll(getInterfaceMethods(apiConfig, cls));
272-
}
326+
// Add parent class methods
327+
methodDocList.addAll(this.getParentsClassMethods(apiConfig, cls));
328+
// Add interface methods
329+
methodDocList.addAll(this.getInterfaceMethods(apiConfig, cls));
330+
331+
Map<JavadocJavaMethod, List<ApiParam>> methodRequestParams = new HashMap<>(16);
332+
Map<JavadocJavaMethod, List<ApiParam>> methodResponseParams = new HashMap<>(16);
333+
334+
// Construct the method map
335+
Map<String, JavadocJavaMethod> methodMap = methodDocList.stream().collect(Collectors.toMap(method -> {
336+
// Build request params
337+
List<ApiParam> requestParams = this.requestParams(method.getJavaMethod(), projectBuilder,
338+
new AtomicInteger(0), method.getActualTypesMap());
339+
methodRequestParams.put(method, requestParams);
340+
// Build response params
341+
List<ApiParam> responseParams = this.buildReturnApiParams(DocJavaMethod.builder()
342+
.setJavaMethod(method.getJavaMethod())
343+
.setActualTypesMap(method.getActualTypesMap()), projectBuilder);
344+
methodResponseParams.put(method, responseParams);
345+
String requestParamsStr = Objects.isNull(requestParams) ? "null"
346+
: requestParams.stream().map(ApiParam::getFullyTypeName).collect(Collectors.joining("|"));
347+
348+
String responseParamsString = (Objects.isNull(responseParams) ? "null"
349+
: responseParams.stream().map(ApiParam::getFullyTypeName).collect(Collectors.joining("|")));
350+
return requestParamsStr + " " + method.getName() + "(" + responseParamsString + ")";
351+
},
352+
353+
Function.identity(), this::mergeJavadocMethods, LinkedHashMap::new));
273354

274355
int methodOrder = 0;
275-
List<JavadocJavaMethod> javadocJavaMethods = new ArrayList<>(methodDocList.size());
276-
for (JavadocJavaMethod method : methodDocList) {
356+
List<JavadocJavaMethod> javadocJavaMethods = new ArrayList<>(methodMap.values().size());
357+
for (JavadocJavaMethod method : methodMap.values()) {
277358
methodOrder++;
278359
method.setOrder(methodOrder);
279-
String methodUid = DocUtil.generateId(clazName + method.getName() + methodOrder);
360+
String methodUid = DocUtil.generateId(clsCanonicalName + method.getName() + methodOrder);
280361
method.setMethodId(methodUid);
281362
// build request params
282-
List<ApiParam> requestParams = requestParams(method.getJavaMethod(), projectBuilder, new AtomicInteger(0),
283-
method.getActualTypesMap());
363+
List<ApiParam> requestParams = methodRequestParams.get(method);
284364
// build response params
285-
List<ApiParam> responseParams = buildReturnApiParams(DocJavaMethod.builder()
286-
.setJavaMethod(method.getJavaMethod())
287-
.setActualTypesMap(method.getActualTypesMap()), projectBuilder);
365+
List<ApiParam> responseParams = methodResponseParams.get(method);
288366
if (apiConfig.isParamsDataToTree()) {
289367
method.setRequestParams(ApiParamTreeUtil.apiParamToTree(requestParams));
290368
method.setResponseParams(ApiParamTreeUtil.apiParamToTree(responseParams));
@@ -298,4 +376,30 @@ default List<? extends JavadocJavaMethod> buildServiceMethod(final JavaClass cls
298376
return javadocJavaMethods;
299377
}
300378

379+
/**
380+
* Merges two JavadocJavaMethod objects. If the existing method lacks certain details
381+
* (description, detail, author, version) that the replacement method has, those
382+
* details are copied from the replacement method to the existing method.
383+
* @param existing The existing JavadocJavaMethod object.
384+
* @param replacement The replacement JavadocJavaMethod object.
385+
* @return The merged JavadocJavaMethod object, with details filled in from the
386+
* replacement method if necessary.
387+
*/
388+
default JavadocJavaMethod mergeJavadocMethods(JavadocJavaMethod existing, JavadocJavaMethod replacement) {
389+
// if existing info is empty and replacement info has desc,replace the info
390+
if (StringUtil.isEmpty(existing.getDesc()) && StringUtil.isNotEmpty(replacement.getDesc())) {
391+
existing.setDesc(replacement.getDesc());
392+
}
393+
if (StringUtil.isEmpty(existing.getDetail()) && StringUtil.isNotEmpty(replacement.getDetail())) {
394+
existing.setDetail(replacement.getDetail());
395+
}
396+
if (StringUtil.isEmpty(existing.getAuthor()) && StringUtil.isNotEmpty(replacement.getAuthor())) {
397+
existing.setAuthor(replacement.getAuthor());
398+
}
399+
if (StringUtil.isEmpty(existing.getVersion()) && StringUtil.isNotEmpty(replacement.getVersion())) {
400+
existing.setVersion(replacement.getVersion());
401+
}
402+
return existing;
403+
}
404+
301405
}

0 commit comments

Comments
 (0)