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

Commit 6d9c121

Browse files
committed
feat(validation): ✨ Add Support for Resolving Placeholders in Validation Messages
- Replace list with sets for validator annotations and excluded annotations - Refactor validation annotation retrieval and processing - Improve placeholder replacement in validation messages - Update copyright year to 2025
1 parent aa53b1b commit 6d9c121

File tree

3 files changed

+113
-41
lines changed

3 files changed

+113
-41
lines changed

src/main/java/com/ly/doc/constants/DocValidatorAnnotationEnum.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2018-2024 smart-doc
2+
* Copyright (C) 2018-2025 smart-doc
33
*
44
* Licensed to the Apache Software Foundation (ASF) under one
55
* or more contributor license agreements. See the NOTICE file
@@ -20,8 +20,8 @@
2020
*/
2121
package com.ly.doc.constants;
2222

23-
import java.util.ArrayList;
24-
import java.util.List;
23+
import java.util.HashSet;
24+
import java.util.Set;
2525

2626
/**
2727
* spring validator annotations
@@ -164,12 +164,26 @@ public enum DocValidatorAnnotationEnum {
164164
this.value = value;
165165
}
166166

167-
public static List<String> listValidatorAnnotations() {
168-
List<String> annotations = new ArrayList<>();
167+
/**
168+
* validator annotations
169+
*/
170+
public static final Set<String> VALIDATOR_ANNOTATIONS = new HashSet<>();
171+
172+
/**
173+
* excluded annotations
174+
*/
175+
public static final Set<String> EXCLUDED_ANNOTATIONS = new HashSet<>();
176+
177+
static {
169178
for (DocValidatorAnnotationEnum annotation : DocValidatorAnnotationEnum.values()) {
170-
annotations.add(annotation.value);
179+
VALIDATOR_ANNOTATIONS.add(annotation.value);
171180
}
172-
return annotations;
181+
182+
EXCLUDED_ANNOTATIONS.add(NOT_BLANK.value);
183+
EXCLUDED_ANNOTATIONS.add(NOT_EMPTY.value);
184+
EXCLUDED_ANNOTATIONS.add(NOT_NULL.value);
185+
EXCLUDED_ANNOTATIONS.add(NULL.value);
186+
EXCLUDED_ANNOTATIONS.add(VALIDATED.value);
173187
}
174188

175189
}

src/main/java/com/ly/doc/utils/JavaClassUtil.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* smart-doc
33
*
4-
* Copyright (C) 2018-2024 smart-doc
4+
* Copyright (C) 2018-2025 smart-doc
55
*
66
* Licensed to the Apache Software Foundation (ASF) under one
77
* or more contributor license agreements. See the NOTICE file
@@ -872,9 +872,8 @@ public static Set<String> getParamGroupJavaClass(List<JavaAnnotation> annotation
872872
return new HashSet<>(0);
873873
}
874874
Set<String> javaClassList = new HashSet<>();
875-
List<String> validates = DocValidatorAnnotationEnum.listValidatorAnnotations();
876875
for (JavaAnnotation javaAnnotation : annotations) {
877-
List<AnnotationValue> annotationValueList = getValidatedAnnotationValues(validates, javaAnnotation);
876+
List<AnnotationValue> annotationValueList = getValidatedAnnotationValues(javaAnnotation);
878877
addGroupClass(annotationValueList, javaClassList, builder);
879878
// When using @Validated and group class is empty, add the Default group
880879
// class;
@@ -899,8 +898,7 @@ public static Set<String> getParamGroupJavaClass(JavaAnnotation javaAnnotation)
899898
return new HashSet<>(0);
900899
}
901900
Set<String> javaClassList = new HashSet<>();
902-
List<String> validates = DocValidatorAnnotationEnum.listValidatorAnnotations();
903-
List<AnnotationValue> annotationValueList = getValidatedAnnotationValues(validates, javaAnnotation);
901+
List<AnnotationValue> annotationValueList = getValidatedAnnotationValues(javaAnnotation);
904902
addGroupClass(annotationValueList, javaClassList);
905903
String simpleAnnotationName = javaAnnotation.getType().getValue();
906904
// add default group
@@ -1047,7 +1045,7 @@ private static void recursionGetAllValidInterface(JavaClass classByName, Set<Str
10471045
* annotation values. If the property name cannot be determined or the annotation does
10481046
* not contain valid validation information, an empty list is returned.
10491047
*/
1050-
private static List<AnnotationValue> getValidatedAnnotationValues(List<String> validates,
1048+
private static List<AnnotationValue> getValidatedAnnotationValues(Set<String> validates,
10511049
JavaAnnotation javaAnnotation) {
10521050
String simpleName = javaAnnotation.getType().getValue();
10531051

@@ -1068,6 +1066,25 @@ else if (validates.contains(simpleName)) {
10681066
return Collections.emptyList();
10691067
}
10701068

1069+
/**
1070+
* Retrieves a list of validated annotation values.
1071+
* <p>
1072+
* This method processes and extracts validation-related information from a given
1073+
* annotation object, based on the list of validation annotation names provided. It
1074+
* first determines the annotation type, then identifies the property name to extract
1075+
* based on the annotation type, and finally retrieves the corresponding annotation
1076+
* values according to that property name.
1077+
* </p>
1078+
* @param javaAnnotation A JavaAnnotation object representing the annotation being
1079+
* processed.
1080+
* @return Returns a list of AnnotationValue objects containing valid validation
1081+
* annotation values. If the property name cannot be determined or the annotation does
1082+
* not contain valid validation information, an empty list is returned.
1083+
*/
1084+
private static List<AnnotationValue> getValidatedAnnotationValues(JavaAnnotation javaAnnotation) {
1085+
return getValidatedAnnotationValues(DocValidatorAnnotationEnum.VALIDATOR_ANNOTATIONS, javaAnnotation);
1086+
}
1087+
10711088
/**
10721089
* Generic parameter map.
10731090
* @param genericMap generic map

src/main/java/com/ly/doc/utils/JavaFieldUtil.java

Lines changed: 69 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,13 @@
3535

3636
import java.lang.reflect.Field;
3737
import java.lang.reflect.Modifier;
38+
import java.util.LinkedHashMap;
3839
import java.util.List;
3940
import java.util.Map;
4041
import java.util.Objects;
42+
import java.util.StringJoiner;
43+
import java.util.regex.Matcher;
44+
import java.util.regex.Pattern;
4145

4246
/**
4347
* JavaFieldUtil
@@ -53,6 +57,11 @@ private JavaFieldUtil() {
5357
throw new IllegalStateException("Utility class");
5458
}
5559

60+
/**
61+
* Pattern to match placeholders in messages
62+
*/
63+
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\{(.+?)\\}");
64+
5665
/**
5766
* public static final
5867
*/
@@ -141,47 +150,79 @@ public static String getParamMaxLength(ClassLoader classLoader, List<JavaAnnotat
141150
}
142151

143152
/**
144-
* getJsr303Comment
145-
* @param showValidation Show JSR validation information
146-
* @param classLoader ClassLoader
147-
* @param annotations annotations
148-
* @return Jsr comments
153+
* Get JSR 303 validation comments.
154+
* @param showValidation Whether to show JSR validation information
155+
* @param classLoader The ClassLoader used to resolve annotation values
156+
* @param annotations List of Java annotations to process
157+
* @return A string containing JSR validation comments
149158
*/
150159
public static String getJsrComment(boolean showValidation, ClassLoader classLoader,
151160
List<JavaAnnotation> annotations) {
152161
if (!showValidation) {
153162
return DocGlobalConstants.EMPTY;
154163
}
155-
StringBuilder sb = new StringBuilder();
164+
StringJoiner validationJoiner = new StringJoiner("; ");
156165
for (JavaAnnotation annotation : annotations) {
157-
Map<String, AnnotationValue> values = annotation.getPropertyMap();
158-
String name = annotation.getType().getValue();
159-
if (JSRAnnotationConstants.NOT_BLANK.equals(name) || JSRAnnotationConstants.NOT_EMPTY.equals(name)
160-
|| JSRAnnotationConstants.NOT_NULL.equals(name) || JSRAnnotationConstants.NULL.equals(name)
161-
|| JSRAnnotationConstants.VALIDATED.equals(name)) {
166+
String annotationName = annotation.getType().getValue();
167+
// Skip excluded annotations
168+
if (DocValidatorAnnotationEnum.EXCLUDED_ANNOTATIONS.contains(annotationName)) {
162169
continue;
163170
}
164-
if (DocValidatorAnnotationEnum.listValidatorAnnotations().contains(name)) {
165-
sb.append(name).append("(");
166-
int j = 0;
167-
for (Map.Entry<String, AnnotationValue> m : values.entrySet()) {
168-
j++;
169-
String value = DocUtil.resolveAnnotationValue(classLoader, m.getValue());
170-
sb.append(m.getKey()).append("=").append(StringUtil.removeDoubleQuotes(value));
171-
if (j < values.size()) {
172-
sb.append(", ");
173-
}
174-
}
175-
sb.append("); ");
171+
172+
// Skip non-validator annotations
173+
if (!DocValidatorAnnotationEnum.VALIDATOR_ANNOTATIONS.contains(annotationName)) {
174+
continue;
176175
}
176+
177+
StringJoiner paramJoiner = getParamJoiner(classLoader, annotation);
178+
179+
validationJoiner.add(annotationName + "(" + paramJoiner + ")");
180+
177181
}
178-
if (sb.length() < 1) {
179-
return DocGlobalConstants.EMPTY;
182+
return validationJoiner.length() == 0 ? DocGlobalConstants.EMPTY : "\nValidation[" + validationJoiner + "]";
183+
}
184+
185+
/**
186+
* Get the string joiner for the given annotation.
187+
* @param classLoader The ClassLoader used to resolve annotation values
188+
* @param annotation The Java annotation to process
189+
* @return A string joiner containing the resolved annotation properties
190+
*/
191+
private static StringJoiner getParamJoiner(ClassLoader classLoader, JavaAnnotation annotation) {
192+
Map<String, AnnotationValue> properties = annotation.getPropertyMap();
193+
Map<String, String> resolvedValues = new LinkedHashMap<>();
194+
properties.forEach((key, value) -> resolvedValues.put(key,
195+
StringUtil.removeDoubleQuotes(DocUtil.resolveAnnotationValue(classLoader, value))));
196+
197+
resolvedValues.computeIfPresent("message", (k, v) -> replacePlaceholders(v, resolvedValues));
198+
199+
StringJoiner paramJoiner = new StringJoiner(", ");
200+
resolvedValues.forEach((key, val) -> paramJoiner.add(key + "=" + val));
201+
return paramJoiner;
202+
}
203+
204+
/**
205+
* Replace placeholders in the message with corresponding annotation property values.
206+
* @param message The original message content
207+
* @param resolvedValues A map of resolved annotation property values
208+
* @return The message with placeholders replaced
209+
*/
210+
private static String replacePlaceholders(String message, Map<String, String> resolvedValues) {
211+
// Early exit if the message is null, empty, or does not contain any placeholders
212+
if (message == null || !message.contains("{") || !message.contains("}")) {
213+
return message;
180214
}
181-
if (sb.toString().contains(";")) {
182-
sb.deleteCharAt(sb.lastIndexOf(";"));
215+
216+
Matcher matcher = PLACEHOLDER_PATTERN.matcher(message);
217+
StringBuffer sb = new StringBuffer();
218+
while (matcher.find()) {
219+
String key = matcher.group(1);
220+
String replacement = resolvedValues.getOrDefault(key, matcher.group());
221+
matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement));
183222
}
184-
return "\nValidation[" + sb + "]";
223+
matcher.appendTail(sb);
224+
return sb.toString();
225+
185226
}
186227

187228
/**

0 commit comments

Comments
 (0)