Skip to content

Commit 21760a8

Browse files
philwebbcbeams
authored andcommitted
Provide alternative message code resolver styles
Introduce new 'style' property to DefaultMessageCodesResolver allowing for alternative message styles. Current styles are PREFIX_ERROR_CODE and POSTFIX_ERROR_CODE. The default style retains existing behavior. Issue: SPR-9707
1 parent a57ff50 commit 21760a8

File tree

2 files changed

+103
-15
lines changed

2 files changed

+103
-15
lines changed

spring-context/src/main/java/org/springframework/validation/DefaultMessageCodesResolver.java

Lines changed: 80 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2008 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,14 +18,18 @@
1818

1919
import java.io.Serializable;
2020
import java.util.ArrayList;
21+
import java.util.Collection;
22+
import java.util.LinkedHashSet;
2123
import java.util.List;
24+
import java.util.Set;
2225

2326
import org.springframework.util.StringUtils;
2427

2528
/**
2629
* Default implementation of the {@link MessageCodesResolver} interface.
2730
*
28-
* <p>Will create two message codes for an object error, in the following order:
31+
* <p>Will create two message codes for an object error, in the following order (when
32+
* using the {@link Style#PREFIX_ERROR_CODE prefixed} {@link #setStyle(Style) style}):
2933
* <ul>
3034
* <li>1.: code + "." + object name
3135
* <li>2.: code
@@ -68,11 +72,16 @@
6872
* <li>7. try "typeMismatch"
6973
* </ul>
7074
*
75+
* <p>By default the {@code errorCode}s will be placed at the beginning of constructed
76+
* message strings. The {@link #setStyle(Style) style} property can be used to specify
77+
* alternative {@link Style styles} of concatination.
78+
*
7179
* <p>In order to group all codes into a specific category within your resource bundles,
7280
* e.g. "validation.typeMismatch.name" instead of the default "typeMismatch.name",
7381
* consider specifying a {@link #setPrefix prefix} to be applied.
7482
*
7583
* @author Juergen Hoeller
84+
* @author Phillip Webb
7685
* @since 1.0.1
7786
*/
7887
@SuppressWarnings("serial")
@@ -83,9 +92,13 @@ public class DefaultMessageCodesResolver implements MessageCodesResolver, Serial
8392
*/
8493
public static final String CODE_SEPARATOR = ".";
8594

95+
private static final Style DEFAULT_STYLE = Style.PREFIX_ERROR_CODE;
96+
8697

8798
private String prefix = "";
8899

100+
private Style style = DEFAULT_STYLE;
101+
89102

90103
/**
91104
* Specify a prefix to be applied to any code built by this resolver.
@@ -96,6 +109,14 @@ public void setPrefix(String prefix) {
96109
this.prefix = (prefix != null ? prefix : "");
97110
}
98111

112+
/**
113+
* Specify the style of message code that will be built by this resolver.
114+
* <p>Default is {@link Style#PREFIX_ERROR_CODE}.
115+
*/
116+
public void setStyle(Style style) {
117+
this.style = (style == null ? DEFAULT_STYLE : style);
118+
}
119+
99120
/**
100121
* Return the prefix to be applied to any code built by this resolver.
101122
* <p>Returns an empty String in case of no prefix.
@@ -106,9 +127,7 @@ protected String getPrefix() {
106127

107128

108129
public String[] resolveMessageCodes(String errorCode, String objectName) {
109-
return new String[] {
110-
postProcessMessageCode(errorCode + CODE_SEPARATOR + objectName),
111-
postProcessMessageCode(errorCode)};
130+
return resolveMessageCodes(errorCode, objectName, "", null);
112131
}
113132

114133
/**
@@ -121,26 +140,54 @@ public String[] resolveMessageCodes(String errorCode, String objectName) {
121140
* @return the list of codes
122141
*/
123142
public String[] resolveMessageCodes(String errorCode, String objectName, String field, Class<?> fieldType) {
124-
List<String> codeList = new ArrayList<String>();
143+
Set<String> codeList = new LinkedHashSet<String>();
125144
List<String> fieldList = new ArrayList<String>();
126145
buildFieldList(field, fieldList);
127-
for (String fieldInList : fieldList) {
128-
codeList.add(postProcessMessageCode(errorCode + CODE_SEPARATOR + objectName + CODE_SEPARATOR + fieldInList));
129-
}
146+
addCodes(codeList, errorCode, objectName, fieldList);
130147
int dotIndex = field.lastIndexOf('.');
131148
if (dotIndex != -1) {
132149
buildFieldList(field.substring(dotIndex + 1), fieldList);
133150
}
134-
for (String fieldInList : fieldList) {
135-
codeList.add(postProcessMessageCode(errorCode + CODE_SEPARATOR + fieldInList));
136-
}
151+
addCodes(codeList, errorCode, null, fieldList);
137152
if (fieldType != null) {
138-
codeList.add(postProcessMessageCode(errorCode + CODE_SEPARATOR + fieldType.getName()));
153+
addCode(codeList, errorCode, null, fieldType.getName());
139154
}
140-
codeList.add(postProcessMessageCode(errorCode));
155+
addCode(codeList, errorCode, null, null);
141156
return StringUtils.toStringArray(codeList);
142157
}
143158

159+
private void addCodes(Collection<String> codeList, String errorCode, String objectName, Iterable<String> fields) {
160+
for (String field : fields) {
161+
addCode(codeList, errorCode, objectName, field);
162+
}
163+
}
164+
165+
private void addCode(Collection<String> codeList, String errorCode, String objectName, String field) {
166+
String code = getCode(errorCode, objectName, field);
167+
codeList.add(postProcessMessageCode(code));
168+
}
169+
170+
private String getCode(String errorCode, String objectName, String field) {
171+
switch (this.style) {
172+
case PREFIX_ERROR_CODE:
173+
return toDelimitedString(errorCode, objectName, field);
174+
case POSTFIX_ERROR_CODE:
175+
return toDelimitedString(objectName, field, errorCode);
176+
}
177+
throw new IllegalStateException("Unknown style " + this.style);
178+
}
179+
180+
private String toDelimitedString(String... elements) {
181+
StringBuilder rtn = new StringBuilder();
182+
for (String element : elements) {
183+
if(StringUtils.hasLength(element)) {
184+
rtn.append(rtn.length() == 0 ? "" : CODE_SEPARATOR);
185+
rtn.append(element);
186+
}
187+
}
188+
return rtn.toString();
189+
}
190+
144191
/**
145192
* Add both keyed and non-keyed entries for the supplied <code>field</code>
146193
* to the supplied field list.
@@ -173,4 +220,23 @@ protected String postProcessMessageCode(String code) {
173220
return getPrefix() + code;
174221
}
175222

223+
224+
/**
225+
* The various styles that can be used to construct message codes.
226+
*/
227+
public static enum Style {
228+
229+
/**
230+
* Prefix the error code at the beginning of the generated message code. eg:
231+
* {@code errorCode + "." + object name + "." + field}
232+
*/
233+
PREFIX_ERROR_CODE,
234+
235+
/**
236+
* Postfix the error code at the end of the generated message code. eg:
237+
* {@code object name + "." + field + "." + errorCode}
238+
*/
239+
POSTFIX_ERROR_CODE
240+
}
241+
176242
}

spring-context/src/test/java/org/springframework/validation/DefaultMessageCodesResolverTests.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import org.junit.Test;
2424
import org.springframework.beans.TestBean;
25+
import org.springframework.validation.DefaultMessageCodesResolver.Style;
2526

2627
/**
2728
* Tests for {@link DefaultMessageCodesResolver}.
@@ -119,5 +120,26 @@ public void shouldSupportNullFieldType() throws Exception {
119120
"errorCode.objectName.field",
120121
"errorCode.field",
121122
"errorCode" })));
122-
}
123+
}
124+
125+
@Test
126+
public void shouldSupportPostfixStyle() throws Exception {
127+
resolver.setStyle(Style.POSTFIX_ERROR_CODE);
128+
String[] codes = resolver.resolveMessageCodes("errorCode", "objectName");
129+
assertThat(codes, is(equalTo(new String[] {
130+
"objectName.errorCode",
131+
"errorCode" })));
132+
}
133+
134+
@Test
135+
public void shouldSupportFieldPostfixStyle() throws Exception {
136+
resolver.setStyle(Style.POSTFIX_ERROR_CODE);
137+
String[] codes = resolver.resolveMessageCodes("errorCode", "objectName", "field",
138+
TestBean.class);
139+
assertThat(codes, is(equalTo(new String[] {
140+
"objectName.field.errorCode",
141+
"field.errorCode",
142+
"org.springframework.beans.TestBean.errorCode",
143+
"errorCode" })));
144+
}
123145
}

0 commit comments

Comments
 (0)