Skip to content

Commit 5cfddaf

Browse files
committed
HV-1831 Do not create unnecessary copies of token lists
Signed-off-by: marko-bekhta <[email protected]>
1 parent 9a786a5 commit 5cfddaf

File tree

4 files changed

+41
-31
lines changed

4 files changed

+41
-31
lines changed

engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/TokenCollector.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import static org.hibernate.validator.internal.engine.messageinterpolation.util.InterpolationHelper.ESCAPE_CHARACTER;
1111
import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
1212

13-
import java.util.Collections;
1413
import java.util.List;
1514

1615
import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTermType;
@@ -114,7 +113,7 @@ public InterpolationTermType getInterpolationType() {
114113
}
115114

116115
public List<Token> getTokenList() {
117-
return Collections.unmodifiableList( tokenList );
116+
return tokenList;
118117
}
119118

120119
public String getOriginalMessageDescriptor() {

engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/TokenIterator.java

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,29 @@
44
*/
55
package org.hibernate.validator.internal.engine.messageinterpolation.parser;
66

7-
import java.util.ArrayList;
87
import java.util.List;
98

9+
import org.hibernate.validator.internal.util.stereotypes.Lazy;
10+
1011
/**
1112
* Allows to iterate over a list of message tokens and replace parameters.
1213
*
1314
* @author Hardy Ferentschik
1415
*/
1516
public class TokenIterator {
1617
private final List<Token> tokenList;
18+
private final String originalMessage;
19+
@Lazy
20+
private StringBuilder messageBuilder;
1721

1822
private int currentPosition;
1923
private Token currentToken;
2024
private boolean allInterpolationTermsProcessed;
2125
private boolean currentTokenAvailable;
2226

23-
public TokenIterator(List<Token> tokens) {
24-
this.tokenList = new ArrayList<Token>( tokens );
27+
public TokenIterator(String originalMessage, List<Token> tokens) {
28+
this.tokenList = tokens;
29+
this.originalMessage = originalMessage;
2530
}
2631

2732
/**
@@ -30,7 +35,6 @@ public TokenIterator(List<Token> tokens) {
3035
* can be called.
3136
*
3237
* @return Returns {@code true} in case there are more message parameters, {@code false} otherwise.
33-
*
3438
* @throws MessageDescriptorFormatException in case the message descriptor is invalid
3539
*/
3640
public boolean hasMoreInterpolationTerms() throws MessageDescriptorFormatException {
@@ -41,6 +45,9 @@ public boolean hasMoreInterpolationTerms() throws MessageDescriptorFormatExcepti
4145
currentTokenAvailable = true;
4246
return true;
4347
}
48+
if ( messageBuilder != null ) {
49+
messageBuilder.append( currentToken.getTokenValue() );
50+
}
4451
}
4552
allInterpolationTermsProcessed = true;
4653
return false;
@@ -65,20 +72,24 @@ public String nextInterpolationTerm() {
6572
* @param replacement The string to replace the current term with.
6673
*/
6774
public void replaceCurrentInterpolationTerm(String replacement) {
68-
Token token = new Token( replacement );
69-
token.terminate();
70-
tokenList.set( currentPosition - 1, token );
75+
if ( !currentToken.getTokenValue().equals( replacement ) ) {
76+
if ( messageBuilder == null ) {
77+
messageBuilder = new StringBuilder();
78+
for ( int i = 0; i < currentPosition - 1; i++ ) {
79+
messageBuilder.append( tokenList.get( i ).getTokenValue() );
80+
}
81+
}
82+
}
83+
if ( messageBuilder != null ) {
84+
messageBuilder.append( replacement );
85+
}
7186
}
7287

7388
public String getInterpolatedMessage() {
7489
if ( !allInterpolationTermsProcessed ) {
7590
throw new IllegalStateException( "Not all interpolation terms have been processed yet." );
7691
}
77-
StringBuilder messageBuilder = new StringBuilder();
78-
for ( Token token : tokenList ) {
79-
messageBuilder.append( token.getTokenValue() );
80-
}
8192

82-
return messageBuilder.toString();
93+
return messageBuilder != null ? messageBuilder.toString() : originalMessage;
8394
}
8495
}

engine/src/main/java/org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ private String interpolateMessage(String message, Context context, Locale locale
404404
if ( resolvedMessage.indexOf( '{' ) > -1 ) {
405405
// resolve parameter expressions (step 2)
406406
resolvedMessage = interpolateExpression(
407-
new TokenIterator( getParameterTokens( resolvedMessage, tokenizedParameterMessages, InterpolationTermType.PARAMETER ) ),
407+
new TokenIterator( resolvedMessage, getParameterTokens( resolvedMessage, tokenizedParameterMessages, InterpolationTermType.PARAMETER ) ),
408408
context,
409409
locale
410410
);
@@ -416,7 +416,7 @@ private String interpolateMessage(String message, Context context, Locale locale
416416
if ( !( context instanceof HibernateMessageInterpolatorContext )
417417
|| ( (HibernateMessageInterpolatorContext) context ).getExpressionLanguageFeatureLevel() != ExpressionLanguageFeatureLevel.NONE ) {
418418
resolvedMessage = interpolateExpression(
419-
new TokenIterator( getParameterTokens( resolvedMessage, tokenizedELMessages, InterpolationTermType.EL ) ),
419+
new TokenIterator( resolvedMessage, getParameterTokens( resolvedMessage, tokenizedELMessages, InterpolationTermType.EL ) ),
420420
context,
421421
locale );
422422
}
@@ -503,7 +503,7 @@ private boolean hasReplacementTakenPlace(String origMessage, String newMessage)
503503
private String interpolateBundleMessage(String message, ResourceBundle bundle, Locale locale, boolean recursive)
504504
throws MessageDescriptorFormatException {
505505
TokenCollector tokenCollector = new TokenCollector( message, InterpolationTermType.PARAMETER );
506-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
506+
TokenIterator tokenIterator = new TokenIterator( message, tokenCollector.getTokenList() );
507507
while ( tokenIterator.hasMoreInterpolationTerms() ) {
508508
String term = tokenIterator.nextInterpolationTerm();
509509
String resolvedParameterValue = resolveParameter(

engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenIteratorTest.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,22 @@ public class TokenIteratorTest {
2424
@Test(expectedExceptions = IllegalStateException.class)
2525
public void testGettingInterpolatedMessageWithoutCallingHasMoreInterpolationTerms() throws Exception {
2626
TokenCollector tokenCollector = new TokenCollector( "foo", InterpolationTermType.PARAMETER );
27-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
27+
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getOriginalMessageDescriptor(), tokenCollector.getTokenList() );
2828
tokenIterator.getInterpolatedMessage();
2929
}
3030

3131
@Test(expectedExceptions = IllegalStateException.class)
3232
public void testNextInterpolationTermWithoutCallingHasMoreInterpolationTerms() throws Exception {
3333
TokenCollector tokenCollector = new TokenCollector( "foo", InterpolationTermType.PARAMETER );
34-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
34+
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getOriginalMessageDescriptor(), tokenCollector.getTokenList() );
3535
tokenIterator.nextInterpolationTerm();
3636
}
3737

3838
@Test
3939
public void testMessageDescriptorWithoutParameter() throws Exception {
4040
String message = "this message has no parameter";
4141
TokenCollector tokenCollector = new TokenCollector( message, InterpolationTermType.PARAMETER );
42-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
42+
TokenIterator tokenIterator = new TokenIterator( message, tokenCollector.getTokenList() );
4343

4444
assertFalse( tokenIterator.hasMoreInterpolationTerms(), "There should be no interpolation terms" );
4545
assertEquals( tokenIterator.getInterpolatedMessage(), message, "The message should be unchanged" );
@@ -48,15 +48,15 @@ public void testMessageDescriptorWithoutParameter() throws Exception {
4848
@Test
4949
public void testParameterTermHasPrecedenceForParameterParser() throws Exception {
5050
TokenCollector tokenCollector = new TokenCollector( "${foo}", InterpolationTermType.PARAMETER );
51-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
51+
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getOriginalMessageDescriptor(), tokenCollector.getTokenList() );
5252
assertSingleReplacement( tokenIterator, "{foo}", "bar", "$bar" );
5353
}
5454

5555
@Test
5656
public void testFindParameterTerms() throws Exception {
5757
String message = "{foo} {bar}";
5858
TokenCollector tokenCollector = new TokenCollector( message, InterpolationTermType.PARAMETER );
59-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
59+
TokenIterator tokenIterator = new TokenIterator( message, tokenCollector.getTokenList() );
6060

6161
assertTrue( tokenIterator.hasMoreInterpolationTerms(), "There should be a term" );
6262
assertEquals( tokenIterator.nextInterpolationTerm(), "{foo}", "{foo} should be the first term" );
@@ -71,7 +71,7 @@ public void testFindParameterTerms() throws Exception {
7171
public void testEscapedMetaCharactersStayUntouched() throws Exception {
7272
String message = "\\} \\{ \\$ \\\\";
7373
TokenCollector tokenCollector = new TokenCollector( message, InterpolationTermType.PARAMETER );
74-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
74+
TokenIterator tokenIterator = new TokenIterator( message, tokenCollector.getTokenList() );
7575

7676
assertFalse( tokenIterator.hasMoreInterpolationTerms(), "There should be no term" );
7777
assertEquals(
@@ -87,7 +87,7 @@ public void testUnEscapedExpressionLanguageLiteral() throws Exception {
8787
"The price is US$ {value}",
8888
InterpolationTermType.PARAMETER
8989
);
90-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
90+
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getOriginalMessageDescriptor(), tokenCollector.getTokenList() );
9191

9292
assertSingleReplacement( tokenIterator, "{value}", "100", "The price is US$ 100" );
9393
}
@@ -98,7 +98,7 @@ public void testEscapedExpressionLanguageLiteralParameterParsing() throws Except
9898
"The price is US\\$ {value}",
9999
InterpolationTermType.PARAMETER
100100
);
101-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
101+
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getOriginalMessageDescriptor(), tokenCollector.getTokenList() );
102102

103103
assertSingleReplacement( tokenIterator, "{value}", "100", "The price is US\\$ 100" );
104104
}
@@ -109,7 +109,7 @@ public void testExpressionLanguageLiteralParameterParsing() throws Exception {
109109
"The price is US$ {value}",
110110
InterpolationTermType.PARAMETER
111111
);
112-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
112+
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getOriginalMessageDescriptor(), tokenCollector.getTokenList() );
113113

114114
assertSingleReplacement( tokenIterator, "{value}", "100", "The price is US$ 100" );
115115
}
@@ -118,7 +118,7 @@ public void testExpressionLanguageLiteralParameterParsing() throws Exception {
118118
public void testExpressionLanguageLiteralELParsing() throws Exception {
119119
String message = "The price is US$ {value}";
120120
TokenCollector tokenCollector = new TokenCollector( message, InterpolationTermType.EL );
121-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
121+
TokenIterator tokenIterator = new TokenIterator( message, tokenCollector.getTokenList() );
122122

123123
assertFalse( tokenIterator.hasMoreInterpolationTerms(), "There should be no interpolation terms" );
124124
assertEquals( tokenIterator.getInterpolatedMessage(), message, "The message should be unchanged" );
@@ -127,31 +127,31 @@ public void testExpressionLanguageLiteralELParsing() throws Exception {
127127
@Test
128128
public void testReplaceParameter() throws Exception {
129129
TokenCollector tokenCollector = new TokenCollector( "{foo}", InterpolationTermType.PARAMETER );
130-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
130+
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getOriginalMessageDescriptor(), tokenCollector.getTokenList() );
131131

132132
assertSingleReplacement( tokenIterator, "{foo}", "bar", "bar" );
133133
}
134134

135135
@Test
136136
public void testReplaceParameterInline() throws Exception {
137137
TokenCollector tokenCollector = new TokenCollector( "a{var}c", InterpolationTermType.PARAMETER );
138-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
138+
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getOriginalMessageDescriptor(), tokenCollector.getTokenList() );
139139

140140
assertSingleReplacement( tokenIterator, "{var}", "b", "abc" );
141141
}
142142

143143
@Test
144144
public void testReplaceParameterInEscapedBraces() throws Exception {
145145
TokenCollector tokenCollector = new TokenCollector( "\\{{var}\\}", InterpolationTermType.PARAMETER );
146-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
146+
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getOriginalMessageDescriptor(), tokenCollector.getTokenList() );
147147

148148
assertSingleReplacement( tokenIterator, "{var}", "foo", "\\{foo\\}" );
149149
}
150150

151151
@Test
152152
public void testELParameter() throws Exception {
153153
TokenCollector tokenCollector = new TokenCollector( "${foo}", InterpolationTermType.EL );
154-
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getTokenList() );
154+
TokenIterator tokenIterator = new TokenIterator( tokenCollector.getOriginalMessageDescriptor(), tokenCollector.getTokenList() );
155155

156156
assertSingleReplacement( tokenIterator, "${foo}", "bar", "bar" );
157157
}

0 commit comments

Comments
 (0)