Skip to content

Commit 38bfb2b

Browse files
committed
Introduce MessageCodeFormatter abstraction
This commit refactors changes introduced in 21760a8 as follows: - Introduce top-level MessageCodeFormatter interface and DefaultMessageCodesResolver#setMessageCodeFormatter property to allow for user-defined message code formatting strategies - Rename DefaultMessageCodesResolver.Style enum => DMCR.Format - Refactor DefaultMessageCodesResolver.Format to implement the new MessageCodeFormatter interface The result is that users have convenient access to common formatting strategies via the Format enum, while retaining the flexibility to provide their own custom MessageCodeFormatter implementation if desired. See DefaultMessageCodesResolverTests#shouldSupport*Format tests for usage examples. Issue: SPR-9707
1 parent 21760a8 commit 38bfb2b

File tree

3 files changed

+107
-43
lines changed

3 files changed

+107
-43
lines changed

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

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
* Default implementation of the {@link MessageCodesResolver} interface.
3030
*
3131
* <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}):
32+
* using the {@link Format#PREFIX_ERROR_CODE prefixed}
33+
* {@link #setMessageCodeFormatter(MessageCodeFormatter) formatter}):
3334
* <ul>
3435
* <li>1.: code + "." + object name
3536
* <li>2.: code
@@ -73,15 +74,17 @@
7374
* </ul>
7475
*
7576
* <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.
77+
* message strings. The {@link #setMessageCodeFormatter(MessageCodeFormatter)
78+
* messageCodeFormatter} property can be used to specify an alternative concatenation
79+
* {@link MessageCodeFormatter format}.
7880
*
7981
* <p>In order to group all codes into a specific category within your resource bundles,
8082
* e.g. "validation.typeMismatch.name" instead of the default "typeMismatch.name",
8183
* consider specifying a {@link #setPrefix prefix} to be applied.
8284
*
8385
* @author Juergen Hoeller
8486
* @author Phillip Webb
87+
* @author Chris Beams
8588
* @since 1.0.1
8689
*/
8790
@SuppressWarnings("serial")
@@ -92,12 +95,12 @@ public class DefaultMessageCodesResolver implements MessageCodesResolver, Serial
9295
*/
9396
public static final String CODE_SEPARATOR = ".";
9497

95-
private static final Style DEFAULT_STYLE = Style.PREFIX_ERROR_CODE;
98+
private static final MessageCodeFormatter DEFAULT_FORMATTER = Format.PREFIX_ERROR_CODE;
9699

97100

98101
private String prefix = "";
99102

100-
private Style style = DEFAULT_STYLE;
103+
private MessageCodeFormatter formatter = DEFAULT_FORMATTER;
101104

102105

103106
/**
@@ -110,11 +113,12 @@ public void setPrefix(String prefix) {
110113
}
111114

112115
/**
113-
* Specify the style of message code that will be built by this resolver.
114-
* <p>Default is {@link Style#PREFIX_ERROR_CODE}.
116+
* Specify the format for message codes built by this resolver.
117+
* <p>The default is {@link Format#PREFIX_ERROR_CODE}.
118+
* @since 3.2
115119
*/
116-
public void setStyle(Style style) {
117-
this.style = (style == null ? DEFAULT_STYLE : style);
120+
public void setMessageCodeFormatter(MessageCodeFormatter formatter) {
121+
this.formatter = (formatter == null ? DEFAULT_FORMATTER : formatter);
118122
}
119123

120124
/**
@@ -163,29 +167,7 @@ private void addCodes(Collection<String> codeList, String errorCode, String obje
163167
}
164168

165169
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();
170+
codeList.add(postProcessMessageCode(this.formatter.format(errorCode, objectName, field)));
189171
}
190172

191173
/**
@@ -222,21 +204,51 @@ protected String postProcessMessageCode(String code) {
222204

223205

224206
/**
225-
* The various styles that can be used to construct message codes.
207+
* Common message code formats.
208+
*
209+
* @author Phil Webb
210+
* @author Chris Beams
211+
* @since 3.2
212+
* @see MessageCodeFormatter
213+
* @see DefaultMessageCodesResolver#setMessageCodeFormatter(MessageCodeFormatter)
226214
*/
227-
public static enum Style {
215+
public static enum Format implements MessageCodeFormatter {
228216

229217
/**
230-
* Prefix the error code at the beginning of the generated message code. eg:
218+
* Prefix the error code at the beginning of the generated message code. e.g.:
231219
* {@code errorCode + "." + object name + "." + field}
232220
*/
233-
PREFIX_ERROR_CODE,
221+
PREFIX_ERROR_CODE {
222+
public String format(String errorCode, String objectName, String field) {
223+
return toDelimitedString(errorCode, objectName, field);
224+
}
225+
},
234226

235227
/**
236-
* Postfix the error code at the end of the generated message code. eg:
228+
* Postfix the error code at the end of the generated message code. e.g.:
237229
* {@code object name + "." + field + "." + errorCode}
238230
*/
239-
POSTFIX_ERROR_CODE
231+
POSTFIX_ERROR_CODE {
232+
public String format(String errorCode, String objectName, String field) {
233+
return toDelimitedString(objectName, field, errorCode);
234+
}
235+
};
236+
237+
/**
238+
* Concatenate the given elements, delimiting each with
239+
* {@link DefaultMessageCodesResolver#CODE_SEPARATOR}, skipping zero-length or
240+
* null elements altogether.
241+
*/
242+
public static String toDelimitedString(String... elements) {
243+
StringBuilder rtn = new StringBuilder();
244+
for (String element : elements) {
245+
if(StringUtils.hasLength(element)) {
246+
rtn.append(rtn.length() == 0 ? "" : CODE_SEPARATOR);
247+
rtn.append(element);
248+
}
249+
}
250+
return rtn.toString();
251+
}
240252
}
241253

242254
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2002-2012 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.validation;
18+
19+
/**
20+
* A strategy interface for formatting message codes.
21+
*
22+
* @author Chris Beams
23+
* @since 3.2
24+
* @see DefaultMessageCodesResolver
25+
*/
26+
public interface MessageCodeFormatter {
27+
28+
/**
29+
* Build and return a message code consisting of the given fields, usually delimited
30+
* by {@link DefaultMessageCodesResolver#CODE_SEPARATOR}.
31+
* @param errorCode e.g.: "typeMismatch"
32+
* @param objectName e.g.: "user"
33+
* @param field e.g. "age"
34+
* @return concatenated message code, e.g.: "typeMismatch.user.age"
35+
* @see DefaultMessageCodesResolver.Format
36+
*/
37+
String format(String errorCode, String objectName, String field);
38+
}

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

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

2727
/**
2828
* Tests for {@link DefaultMessageCodesResolver}.
@@ -123,17 +123,17 @@ public void shouldSupportNullFieldType() throws Exception {
123123
}
124124

125125
@Test
126-
public void shouldSupportPostfixStyle() throws Exception {
127-
resolver.setStyle(Style.POSTFIX_ERROR_CODE);
126+
public void shouldSupportPostfixFormat() throws Exception {
127+
resolver.setMessageCodeFormatter(Format.POSTFIX_ERROR_CODE);
128128
String[] codes = resolver.resolveMessageCodes("errorCode", "objectName");
129129
assertThat(codes, is(equalTo(new String[] {
130130
"objectName.errorCode",
131131
"errorCode" })));
132132
}
133133

134134
@Test
135-
public void shouldSupportFieldPostfixStyle() throws Exception {
136-
resolver.setStyle(Style.POSTFIX_ERROR_CODE);
135+
public void shouldSupportFieldPostfixFormat() throws Exception {
136+
resolver.setMessageCodeFormatter(Format.POSTFIX_ERROR_CODE);
137137
String[] codes = resolver.resolveMessageCodes("errorCode", "objectName", "field",
138138
TestBean.class);
139139
assertThat(codes, is(equalTo(new String[] {
@@ -142,4 +142,18 @@ public void shouldSupportFieldPostfixStyle() throws Exception {
142142
"org.springframework.beans.TestBean.errorCode",
143143
"errorCode" })));
144144
}
145+
146+
@Test
147+
public void shouldSupportCustomFormat() throws Exception {
148+
resolver.setMessageCodeFormatter(new MessageCodeFormatter() {
149+
public String format(String errorCode, String objectName, String field) {
150+
return DefaultMessageCodesResolver.Format.toDelimitedString(
151+
"CUSTOM-" + errorCode, objectName, field);
152+
}
153+
});
154+
String[] codes = resolver.resolveMessageCodes("errorCode", "objectName");
155+
assertThat(codes, is(equalTo(new String[] {
156+
"CUSTOM-errorCode.objectName",
157+
"CUSTOM-errorCode" })));
158+
}
145159
}

0 commit comments

Comments
 (0)