Skip to content

Commit

Permalink
Merge pull request #935 from linwumingshi/fix/constant
Browse files Browse the repository at this point in the history
fix: 🐛 resolve incorrect parsing of constants in `@JsonProperty` and `@JSONField` annotation
  • Loading branch information
shalousun authored Oct 31, 2024
2 parents 13b8e04 + cd81eaf commit 2a15d8b
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 39 deletions.
10 changes: 8 additions & 2 deletions src/main/java/com/ly/doc/handler/IWebSocketRequestHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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;
}
Expand All @@ -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<String> subProtocols = JavaClassUtil.getAnnotationValueStrings(projectBuilder, javaAnnotation,
"subprotocols");
builder.setSubProtocols(subProtocols);

// Handle 'decoders' property
builder.setDecoders(JavaClassUtil.getAnnotationValueClassNames(javaAnnotation, "decoders"));
Expand Down
12 changes: 7 additions & 5 deletions src/main/java/com/ly/doc/helper/BaseHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -176,18 +176,20 @@ 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);
}
}

// Handle @JsonProperty
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) {
Expand Down
21 changes: 18 additions & 3 deletions src/main/java/com/ly/doc/template/IWebSocketTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
100 changes: 79 additions & 21 deletions src/main/java/com/ly/doc/utils/DocUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,78 @@
*/
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;
import org.apache.commons.codec.digest.DigestUtils;
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;
Expand Down Expand Up @@ -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);

}

/**
Expand Down
46 changes: 38 additions & 8 deletions src/main/java/com/ly/doc/utils/JavaClassUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -1323,11 +1351,13 @@ public static List<String> 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.
* </p>
* @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<String> getAnnotationValueStrings(JavaAnnotation javaAnnotation, String propertyName) {
public static List<String> getAnnotationValueStrings(ProjectDocConfigBuilder projectBuilder,
JavaAnnotation javaAnnotation, String propertyName) {
AnnotationValue propertyValue = javaAnnotation.getProperty(propertyName);
if (propertyValue != null) {
if (propertyValue instanceof AnnotationValueList) {
Expand All @@ -1338,9 +1368,9 @@ public static List<String> 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();
}
Expand Down

0 comments on commit 2a15d8b

Please sign in to comment.