From cd81eafbd0f1bcad09259cedb75244bf46ea45de Mon Sep 17 00:00:00 2001 From: linwumingshi Date: Tue, 29 Oct 2024 18:04:57 +0800 Subject: [PATCH] fix: :bug: resolve incorrect parsing of constants in `@JsonProperty` and `@JSONField` annotations - Fixed an issue where constants from a constant class in `@JsonProperty` and `@JSONField` annotations were not correctly parsed in generated documentation - Fixed an issue where constants from a constant class in `@ServerEndpoint` subprotocols value [issues 934](https://github.com/TongchengOpenSource/smart-doc/issues/934) --- .../doc/handler/IWebSocketRequestHandler.java | 10 +- .../java/com/ly/doc/helper/BaseHelper.java | 12 ++- .../ly/doc/template/IWebSocketTemplate.java | 21 +++- src/main/java/com/ly/doc/utils/DocUtil.java | 100 ++++++++++++++---- .../java/com/ly/doc/utils/JavaClassUtil.java | 46 ++++++-- 5 files changed, 150 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/ly/doc/handler/IWebSocketRequestHandler.java b/src/main/java/com/ly/doc/handler/IWebSocketRequestHandler.java index 21105f36..1bd304ad 100644 --- a/src/main/java/com/ly/doc/handler/IWebSocketRequestHandler.java +++ b/src/main/java/com/ly/doc/handler/IWebSocketRequestHandler.java @@ -20,6 +20,7 @@ */ package com.ly.doc.handler; +import com.ly.doc.builder.ProjectDocConfigBuilder; import com.ly.doc.constants.DocAnnotationConstants; import com.ly.doc.constants.DocTags; import com.ly.doc.model.request.ServerEndpoint; @@ -28,6 +29,7 @@ import com.thoughtworks.qdox.model.JavaAnnotation; import com.thoughtworks.qdox.model.JavaClass; +import java.util.List; import java.util.Objects; import java.util.Optional; @@ -40,11 +42,13 @@ public interface IWebSocketRequestHandler { /** * handle class annotation `@ServerEndpoint` + * @param projectBuilder the project configuration builder * @param javaAnnotation javaAnnotation @ServerEndpoint * @param cls JavaClass * @return ServerEndpoint */ - default ServerEndpoint handleServerEndpoint(JavaClass cls, JavaAnnotation javaAnnotation) { + default ServerEndpoint handleServerEndpoint(ProjectDocConfigBuilder projectBuilder, JavaClass cls, + JavaAnnotation javaAnnotation) { if (Objects.nonNull(cls.getTagByName(DocTags.IGNORE))) { return null; } @@ -56,7 +60,9 @@ default ServerEndpoint handleServerEndpoint(JavaClass cls, JavaAnnotation javaAn .ifPresent(builder::setUrl); // get subProtocols of annotation - builder.setSubProtocols(JavaClassUtil.getAnnotationValueStrings(javaAnnotation, "subprotocols")); + List subProtocols = JavaClassUtil.getAnnotationValueStrings(projectBuilder, javaAnnotation, + "subprotocols"); + builder.setSubProtocols(subProtocols); // Handle 'decoders' property builder.setDecoders(JavaClassUtil.getAnnotationValueClassNames(javaAnnotation, "decoders")); diff --git a/src/main/java/com/ly/doc/helper/BaseHelper.java b/src/main/java/com/ly/doc/helper/BaseHelper.java index 591af02a..bc8a7d49 100644 --- a/src/main/java/com/ly/doc/helper/BaseHelper.java +++ b/src/main/java/com/ly/doc/helper/BaseHelper.java @@ -176,8 +176,10 @@ protected static FieldJsonAnnotationInfo getFieldJsonAnnotationInfo(ProjectDocCo // Handle @JSONField if (DocAnnotationConstants.SHORT_JSON_FIELD.equals(annotationName)) { if (null != annotation.getProperty(DocAnnotationConstants.NAME_PROP)) { - fieldJsonAnnotationInfo.setFieldName(StringUtil - .removeQuotes(annotation.getProperty(DocAnnotationConstants.NAME_PROP).toString())); + AnnotationValue annotationValue = annotation.getProperty(DocAnnotationConstants.NAME_PROP); + String fieldName = DocUtil.resolveAnnotationValue(projectBuilder.getApiConfig().getClassLoader(), + annotationValue); + fieldJsonAnnotationInfo.setFieldName(fieldName); } } @@ -185,9 +187,9 @@ protected static FieldJsonAnnotationInfo getFieldJsonAnnotationInfo(ProjectDocCo else if (DocAnnotationConstants.SHORT_JSON_PROPERTY.equals(annotationName) || DocAnnotationConstants.GSON_ALIAS_NAME.equals(annotationName)) { AnnotationValue annotationValue = annotation.getProperty(DocAnnotationConstants.VALUE_PROP); - if (null != annotationValue) { - fieldJsonAnnotationInfo.setFieldName(StringUtil.removeQuotes(annotationValue.toString())); - } + String fieldName = DocUtil.resolveAnnotationValue(projectBuilder.getApiConfig().getClassLoader(), + annotationValue); + fieldJsonAnnotationInfo.setFieldName(fieldName); } // Handle JSR303 required if (JavaClassValidateUtil.isJSR303Required(annotationName) && !isResp) { diff --git a/src/main/java/com/ly/doc/template/IWebSocketTemplate.java b/src/main/java/com/ly/doc/template/IWebSocketTemplate.java index a2b04100..f953acbd 100644 --- a/src/main/java/com/ly/doc/template/IWebSocketTemplate.java +++ b/src/main/java/com/ly/doc/template/IWebSocketTemplate.java @@ -39,10 +39,25 @@ import com.ly.doc.utils.JavaClassUtil; import com.power.common.util.StringUtil; import com.power.common.util.ValidateUtil; -import com.thoughtworks.qdox.model.*; +import com.thoughtworks.qdox.model.JavaAnnotation; +import com.thoughtworks.qdox.model.JavaClass; +import com.thoughtworks.qdox.model.JavaMethod; +import com.thoughtworks.qdox.model.JavaParameter; +import com.thoughtworks.qdox.model.JavaType; import com.thoughtworks.qdox.model.impl.DefaultJavaParameterizedType; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -157,7 +172,7 @@ default WebSocketDoc buildEntryPointWebSocketDoc(final JavaClass javaClass, Proj webSocketRequestHandler = Objects.isNull(webSocketRequestHandler) ? DefaultWebSocketRequestHandler.getInstance() : webSocketRequestHandler; - ServerEndpoint serverEndpoint = webSocketRequestHandler.handleServerEndpoint(javaClass, + ServerEndpoint serverEndpoint = webSocketRequestHandler.handleServerEndpoint(projectBuilder, javaClass, serverEndpointAnnotation); WebSocketDoc webSocketDoc = new WebSocketDoc(); diff --git a/src/main/java/com/ly/doc/utils/DocUtil.java b/src/main/java/com/ly/doc/utils/DocUtil.java index 58216a13..362f0492 100644 --- a/src/main/java/com/ly/doc/utils/DocUtil.java +++ b/src/main/java/com/ly/doc/utils/DocUtil.java @@ -20,16 +20,42 @@ */ package com.ly.doc.utils; -import com.ly.doc.constants.*; +import com.ly.doc.constants.DocAnnotationConstants; +import com.ly.doc.constants.DocGlobalConstants; +import com.ly.doc.constants.DocTags; +import com.ly.doc.constants.JAXRSAnnotations; +import com.ly.doc.constants.JakartaJaxrsAnnotations; +import com.ly.doc.constants.JavaTypeConstants; +import com.ly.doc.constants.MediaType; import com.ly.doc.extension.dict.DictionaryValuesResolver; -import com.ly.doc.model.*; +import com.ly.doc.model.ApiConfig; +import com.ly.doc.model.ApiDataDictionary; +import com.ly.doc.model.ApiDocDict; +import com.ly.doc.model.ApiErrorCode; +import com.ly.doc.model.ApiErrorCodeDictionary; +import com.ly.doc.model.ApiReqParam; +import com.ly.doc.model.DataDict; +import com.ly.doc.model.DocJavaField; +import com.ly.doc.model.FormData; +import com.ly.doc.model.SystemPlaceholders; import com.ly.doc.model.request.RequestMapping; import com.mifmif.common.regex.Generex; -import com.power.common.util.*; +import com.power.common.util.CollectionUtil; +import com.power.common.util.DateTimeUtil; +import com.power.common.util.EnumUtil; +import com.power.common.util.IDCardUtil; +import com.power.common.util.RandomUtil; +import com.power.common.util.StringUtil; import com.thoughtworks.qdox.JavaProjectBuilder; -import com.thoughtworks.qdox.model.*; +import com.thoughtworks.qdox.model.DocletTag; +import com.thoughtworks.qdox.model.JavaAnnotation; +import com.thoughtworks.qdox.model.JavaClass; +import com.thoughtworks.qdox.model.JavaField; +import com.thoughtworks.qdox.model.JavaMember; +import com.thoughtworks.qdox.model.JavaMethod; import com.thoughtworks.qdox.model.expression.Add; import com.thoughtworks.qdox.model.expression.AnnotationValue; +import com.thoughtworks.qdox.model.expression.Constant; import com.thoughtworks.qdox.model.expression.Expression; import com.thoughtworks.qdox.model.expression.FieldRef; import net.datafaker.Faker; @@ -37,9 +63,35 @@ import org.apache.commons.lang3.StringUtils; import java.text.DecimalFormat; -import java.time.*; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.MonthDay; +import java.time.OffsetDateTime; +import java.time.Year; +import java.time.YearMonth; +import java.time.ZoneId; import java.time.format.DateTimeFormatter; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.Stack; +import java.util.TimeZone; +import java.util.UUID; import java.util.logging.Logger; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -969,30 +1021,36 @@ public static String getPathUrl(ClassLoader classLoader, JavaAnnotation annotati * @return annotation value */ public static String resolveAnnotationValue(ClassLoader classLoader, AnnotationValue annotationValue) { + // if it is a constant, return its value directly + if (annotationValue instanceof Constant) { + return ((Constant) annotationValue).getValue().toString(); + } + // if it is an add operation, return the result directly if (annotationValue instanceof Add) { Add add = (Add) annotationValue; String leftValue = resolveAnnotationValue(classLoader, add.getLeft()); String rightValue = resolveAnnotationValue(classLoader, add.getRight()); return StringUtil.removeQuotes(leftValue + rightValue); } - else { - if (annotationValue instanceof FieldRef) { - FieldRef fieldRef = (FieldRef) annotationValue; - JavaField javaField = fieldRef.getField(); - if (javaField != null) { - String fieldValue = JavaFieldUtil.getConstantsFieldValue(classLoader, javaField.getDeclaringClass(), - javaField.getName()); - if (StringUtil.isNotEmpty(fieldValue)) { - return StringUtil.removeQuotes(fieldValue); - } - return StringUtil.removeQuotes(javaField.getInitializationExpression()); + // if it is a field reference, return its value directly + if (annotationValue instanceof FieldRef) { + FieldRef fieldRef = (FieldRef) annotationValue; + JavaField javaField = fieldRef.getField(); + if (javaField != null) { + String fieldValue = JavaFieldUtil.getConstantsFieldValue(classLoader, javaField.getDeclaringClass(), + javaField.getName()); + if (StringUtil.isNotEmpty(fieldValue)) { + return StringUtil.removeQuotes(fieldValue); } + return StringUtil.removeQuotes(javaField.getInitializationExpression()); } - return Optional.ofNullable(annotationValue) - .map(Expression::getParameterValue) - .map(Object::toString) - .orElse(StringUtil.EMPTY); } + // default return + return Optional.ofNullable(annotationValue) + .map(Expression::getParameterValue) + .map(Object::toString) + .orElse(StringUtil.EMPTY); + } /** diff --git a/src/main/java/com/ly/doc/utils/JavaClassUtil.java b/src/main/java/com/ly/doc/utils/JavaClassUtil.java index ff311fa9..01b5e524 100644 --- a/src/main/java/com/ly/doc/utils/JavaClassUtil.java +++ b/src/main/java/com/ly/doc/utils/JavaClassUtil.java @@ -23,7 +23,13 @@ package com.ly.doc.utils; import com.ly.doc.builder.ProjectDocConfigBuilder; -import com.ly.doc.constants.*; +import com.ly.doc.constants.DefaultClassConstants; +import com.ly.doc.constants.DocAnnotationConstants; +import com.ly.doc.constants.DocGlobalConstants; +import com.ly.doc.constants.DocTags; +import com.ly.doc.constants.DocValidatorAnnotationEnum; +import com.ly.doc.constants.JSRAnnotationConstants; +import com.ly.doc.constants.JavaTypeConstants; import com.ly.doc.model.ApiConfig; import com.ly.doc.model.ApiDataDictionary; import com.ly.doc.model.DocJavaField; @@ -34,7 +40,15 @@ import com.power.common.util.EnumUtil; import com.power.common.util.StringUtil; import com.thoughtworks.qdox.JavaProjectBuilder; -import com.thoughtworks.qdox.model.*; +import com.thoughtworks.qdox.model.DocletTag; +import com.thoughtworks.qdox.model.JavaAnnotation; +import com.thoughtworks.qdox.model.JavaClass; +import com.thoughtworks.qdox.model.JavaField; +import com.thoughtworks.qdox.model.JavaGenericDeclaration; +import com.thoughtworks.qdox.model.JavaMethod; +import com.thoughtworks.qdox.model.JavaParameterizedType; +import com.thoughtworks.qdox.model.JavaType; +import com.thoughtworks.qdox.model.JavaTypeVariable; import com.thoughtworks.qdox.model.expression.AnnotationValue; import com.thoughtworks.qdox.model.expression.AnnotationValueList; import com.thoughtworks.qdox.model.expression.Constant; @@ -43,8 +57,22 @@ import com.thoughtworks.qdox.model.impl.DefaultJavaParameterizedType; import org.apache.commons.lang3.StringUtils; -import java.lang.reflect.*; -import java.util.*; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -1323,11 +1351,13 @@ public static List getAnnotationValueClassNames(JavaAnnotation javaAnnot * annotation. It handles both single string values and lists of string values. If the * property is not found or has no valid string values, an empty list is returned. *

+ * @param projectBuilder the project configuration builder * @param javaAnnotation the annotation containing the property * @param propertyName the name of the property to retrieve * @return a list of string values or an empty list if not present */ - public static List getAnnotationValueStrings(JavaAnnotation javaAnnotation, String propertyName) { + public static List getAnnotationValueStrings(ProjectDocConfigBuilder projectBuilder, + JavaAnnotation javaAnnotation, String propertyName) { AnnotationValue propertyValue = javaAnnotation.getProperty(propertyName); if (propertyValue != null) { if (propertyValue instanceof AnnotationValueList) { @@ -1338,9 +1368,9 @@ public static List getAnnotationValueStrings(JavaAnnotation javaAnnotati .filter(StringUtil::isNotEmpty) .collect(Collectors.toList()); } - if (propertyValue instanceof Constant) { - return Collections.singletonList(((Constant) propertyValue).getValue().toString()); - } + String value = DocUtil.resolveAnnotationValue(projectBuilder.getApiConfig().getClassLoader(), + propertyValue); + return Collections.singletonList(value); } return Collections.emptyList(); }