442 lines
20 KiB
Diff
442 lines
20 KiB
Diff
From 72049532fa289d0f5664e9b1a7fd19fd514e595f Mon Sep 17 00:00:00 2001
|
|
From: Gunnar Morling <gunnar.morling@googlemail.com>
|
|
Date: Wed, 31 Aug 2016 15:28:25 +0200
|
|
Subject: [PATCH] HV-1091 Using iterative instead of recursive approach for
|
|
message parsing
|
|
|
|
---
|
|
.../parser/BeginState.java | 12 +--
|
|
.../messageinterpolation/parser/ELState.java | 10 ---
|
|
.../parser/EscapedState.java | 9 --
|
|
.../parser/InterpolationTermState.java | 12 ---
|
|
.../parser/MessageState.java | 11 ---
|
|
.../parser/ParserState.java | 2 -
|
|
.../parser/TokenCollector.java | 16 ++--
|
|
.../validator/bugs/TooBigMessageTest.java | 90 +++++++++++++++++++
|
|
.../TokenIteratorTest.java | 8 +-
|
|
9 files changed, 103 insertions(+), 67 deletions(-)
|
|
create mode 100644 engine/src/test/java/org/hibernate/validator/bugs/TooBigMessageTest.java
|
|
|
|
diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/BeginState.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/BeginState.java
|
|
index 678f16e46..5aee73b9f 100644
|
|
--- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/BeginState.java
|
|
+++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/BeginState.java
|
|
@@ -18,11 +18,7 @@
|
|
|
|
@Override
|
|
public void terminate(TokenCollector tokenCollector) throws MessageDescriptorFormatException {
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void start(TokenCollector tokenCollector) throws MessageDescriptorFormatException {
|
|
- tokenCollector.next();
|
|
+ tokenCollector.terminateToken();
|
|
}
|
|
|
|
@Override
|
|
@@ -30,7 +26,6 @@ public void handleNonMetaCharacter(char character, TokenCollector tokenCollector
|
|
throws MessageDescriptorFormatException {
|
|
tokenCollector.appendToToken( character );
|
|
tokenCollector.transitionState( new MessageState() );
|
|
- tokenCollector.next();
|
|
}
|
|
|
|
@Override
|
|
@@ -43,7 +38,6 @@ public void handleBeginTerm(char character, TokenCollector tokenCollector) throw
|
|
tokenCollector.makeParameterToken();
|
|
}
|
|
tokenCollector.transitionState( new InterpolationTermState() );
|
|
- tokenCollector.next();
|
|
}
|
|
|
|
@Override
|
|
@@ -56,7 +50,6 @@ public void handleEscapeCharacter(char character, TokenCollector tokenCollector)
|
|
throws MessageDescriptorFormatException {
|
|
tokenCollector.appendToToken( character );
|
|
tokenCollector.transitionState( new EscapedState( this ) );
|
|
- tokenCollector.next();
|
|
}
|
|
|
|
@Override
|
|
@@ -68,9 +61,6 @@ public void handleELDesignator(char character, TokenCollector tokenCollector)
|
|
else {
|
|
ParserState state = new ELState();
|
|
tokenCollector.transitionState( state );
|
|
- tokenCollector.next();
|
|
}
|
|
}
|
|
}
|
|
-
|
|
-
|
|
diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/ELState.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/ELState.java
|
|
index ce7df402f..569ea1e2e 100644
|
|
--- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/ELState.java
|
|
+++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/ELState.java
|
|
@@ -15,11 +15,6 @@
|
|
public class ELState implements ParserState {
|
|
private static final Log log = LoggerFactory.make();
|
|
|
|
- @Override
|
|
- public void start(TokenCollector tokenCollector) {
|
|
- throw new IllegalStateException( "Parsing of message descriptor cannot start in this state" );
|
|
- }
|
|
-
|
|
@Override
|
|
public void terminate(TokenCollector tokenCollector) throws MessageDescriptorFormatException {
|
|
tokenCollector.appendToToken( TokenCollector.EL_DESIGNATOR );
|
|
@@ -33,7 +28,6 @@ public void handleNonMetaCharacter(char character, TokenCollector tokenCollector
|
|
tokenCollector.appendToToken( character );
|
|
tokenCollector.terminateToken();
|
|
tokenCollector.transitionState( new BeginState() );
|
|
- tokenCollector.next();
|
|
}
|
|
|
|
@Override
|
|
@@ -44,7 +38,6 @@ public void handleBeginTerm(char character, TokenCollector tokenCollector) throw
|
|
tokenCollector.appendToToken( character );
|
|
tokenCollector.makeELToken();
|
|
tokenCollector.transitionState( new InterpolationTermState() );
|
|
- tokenCollector.next();
|
|
}
|
|
|
|
@Override
|
|
@@ -59,7 +52,6 @@ public void handleEndTerm(char character, TokenCollector tokenCollector) throws
|
|
public void handleEscapeCharacter(char character, TokenCollector tokenCollector)
|
|
throws MessageDescriptorFormatException {
|
|
tokenCollector.transitionState( new EscapedState( this ) );
|
|
- tokenCollector.next();
|
|
}
|
|
|
|
@Override
|
|
@@ -68,5 +60,3 @@ public void handleELDesignator(char character, TokenCollector tokenCollector)
|
|
handleNonMetaCharacter( character, tokenCollector );
|
|
}
|
|
}
|
|
-
|
|
-
|
|
diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/EscapedState.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/EscapedState.java
|
|
index 901acff4f..f44242dd1 100644
|
|
--- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/EscapedState.java
|
|
+++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/EscapedState.java
|
|
@@ -16,11 +16,6 @@ public EscapedState(ParserState previousState) {
|
|
this.previousState = previousState;
|
|
}
|
|
|
|
- @Override
|
|
- public void start(TokenCollector tokenCollector) {
|
|
- throw new IllegalStateException( "Parsing of message descriptor cannot start in this state" );
|
|
- }
|
|
-
|
|
@Override
|
|
public void terminate(TokenCollector tokenCollector) throws MessageDescriptorFormatException {
|
|
tokenCollector.terminateToken();
|
|
@@ -58,9 +53,5 @@ private void handleEscapedCharacter(char character, TokenCollector tokenCollecto
|
|
throws MessageDescriptorFormatException {
|
|
tokenCollector.appendToToken( character );
|
|
tokenCollector.transitionState( previousState );
|
|
- tokenCollector.next();
|
|
}
|
|
}
|
|
-
|
|
-
|
|
-
|
|
diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/InterpolationTermState.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/InterpolationTermState.java
|
|
index f921113f9..11c974373 100644
|
|
--- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/InterpolationTermState.java
|
|
+++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/InterpolationTermState.java
|
|
@@ -15,11 +15,6 @@
|
|
public class InterpolationTermState implements ParserState {
|
|
private static final Log log = LoggerFactory.make();
|
|
|
|
- @Override
|
|
- public void start(TokenCollector tokenCollector) {
|
|
- throw new IllegalStateException( "Parsing of message descriptor cannot start in this state" );
|
|
- }
|
|
-
|
|
@Override
|
|
public void terminate(TokenCollector tokenCollector) throws MessageDescriptorFormatException {
|
|
throw log.getNonTerminatedParameterException(
|
|
@@ -32,7 +27,6 @@ public void terminate(TokenCollector tokenCollector) throws MessageDescriptorFor
|
|
public void handleNonMetaCharacter(char character, TokenCollector tokenCollector)
|
|
throws MessageDescriptorFormatException {
|
|
tokenCollector.appendToToken( character );
|
|
- tokenCollector.next();
|
|
}
|
|
|
|
@Override
|
|
@@ -46,7 +40,6 @@ public void handleEndTerm(char character, TokenCollector tokenCollector) throws
|
|
tokenCollector.terminateToken();
|
|
BeginState beginState = new BeginState();
|
|
tokenCollector.transitionState( beginState );
|
|
- tokenCollector.next();
|
|
}
|
|
|
|
@Override
|
|
@@ -55,16 +48,11 @@ public void handleEscapeCharacter(char character, TokenCollector tokenCollector)
|
|
tokenCollector.appendToToken( character );
|
|
ParserState state = new EscapedState( this );
|
|
tokenCollector.transitionState( state );
|
|
- tokenCollector.next();
|
|
-
|
|
}
|
|
|
|
@Override
|
|
public void handleELDesignator(char character, TokenCollector tokenCollector)
|
|
throws MessageDescriptorFormatException {
|
|
tokenCollector.appendToToken( character );
|
|
- tokenCollector.next();
|
|
}
|
|
}
|
|
-
|
|
-
|
|
diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/MessageState.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/MessageState.java
|
|
index bac9c7e46..37cd23503 100644
|
|
--- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/MessageState.java
|
|
+++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/MessageState.java
|
|
@@ -16,11 +16,6 @@
|
|
public class MessageState implements ParserState {
|
|
private static final Log log = LoggerFactory.make();
|
|
|
|
- @Override
|
|
- public void start(TokenCollector tokenCollector) {
|
|
- throw new IllegalStateException( "The parsing of the message descriptor cannot start in this state." );
|
|
- }
|
|
-
|
|
@Override
|
|
public void terminate(TokenCollector tokenCollector) throws MessageDescriptorFormatException {
|
|
tokenCollector.terminateToken();
|
|
@@ -30,7 +25,6 @@ public void terminate(TokenCollector tokenCollector) throws MessageDescriptorFor
|
|
public void handleNonMetaCharacter(char character, TokenCollector tokenCollector)
|
|
throws MessageDescriptorFormatException {
|
|
tokenCollector.appendToToken( character );
|
|
- tokenCollector.next();
|
|
}
|
|
|
|
@Override
|
|
@@ -42,7 +36,6 @@ public void handleBeginTerm(char character, TokenCollector tokenCollector) throw
|
|
tokenCollector.makeParameterToken();
|
|
}
|
|
tokenCollector.transitionState( new InterpolationTermState() );
|
|
- tokenCollector.next();
|
|
}
|
|
|
|
@Override
|
|
@@ -59,7 +52,6 @@ public void handleEscapeCharacter(char character, TokenCollector tokenCollector)
|
|
tokenCollector.appendToToken( character );
|
|
|
|
tokenCollector.transitionState( new EscapedState( this ) );
|
|
- tokenCollector.next();
|
|
}
|
|
|
|
@Override
|
|
@@ -70,9 +62,6 @@ public void handleELDesignator(char character, TokenCollector tokenCollector)
|
|
}
|
|
else {
|
|
tokenCollector.transitionState( new ELState() );
|
|
- tokenCollector.next();
|
|
}
|
|
}
|
|
}
|
|
-
|
|
-
|
|
diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/ParserState.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/ParserState.java
|
|
index a48a48f2b..64317de46 100644
|
|
--- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/ParserState.java
|
|
+++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/ParserState.java
|
|
@@ -13,7 +13,6 @@
|
|
* @author Hardy Ferentschik
|
|
*/
|
|
public interface ParserState {
|
|
- void start(TokenCollector tokenCollector) throws MessageDescriptorFormatException;
|
|
|
|
void terminate(TokenCollector tokenCollector) throws MessageDescriptorFormatException;
|
|
|
|
@@ -27,4 +26,3 @@
|
|
|
|
void handleELDesignator(char character, TokenCollector tokenCollector) throws MessageDescriptorFormatException;
|
|
}
|
|
-
|
|
diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/TokenCollector.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/TokenCollector.java
|
|
index 1443eee7f..ffce7bb68 100644
|
|
--- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/TokenCollector.java
|
|
+++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/TokenCollector.java
|
|
@@ -6,13 +6,13 @@
|
|
*/
|
|
package org.hibernate.validator.internal.engine.messageinterpolation.parser;
|
|
|
|
+import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
|
|
+
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
|
|
import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTermType;
|
|
|
|
-import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
|
|
-
|
|
/**
|
|
* Used to creates a list of tokens from a message descriptor.
|
|
*
|
|
@@ -28,7 +28,7 @@
|
|
private final String originalMessageDescriptor;
|
|
private final InterpolationTermType interpolationTermType;
|
|
|
|
- private List<Token> tokenList;
|
|
+ private final List<Token> tokenList;
|
|
private ParserState currentParserState;
|
|
private int currentPosition;
|
|
private Token currentToken;
|
|
@@ -69,10 +69,11 @@ public void makeELToken() {
|
|
currentToken.makeELToken();
|
|
}
|
|
|
|
- public void next() throws MessageDescriptorFormatException {
|
|
+ private void next() throws MessageDescriptorFormatException {
|
|
if ( currentPosition == originalMessageDescriptor.length() ) {
|
|
// give the current context the chance to complete
|
|
currentParserState.terminate( this );
|
|
+ currentPosition++;
|
|
return;
|
|
}
|
|
char currentCharacter = originalMessageDescriptor.charAt( currentPosition );
|
|
@@ -98,12 +99,12 @@ public void next() throws MessageDescriptorFormatException {
|
|
currentParserState.handleNonMetaCharacter( currentCharacter, this );
|
|
}
|
|
}
|
|
- // make sure the last token is terminated
|
|
- terminateToken();
|
|
}
|
|
|
|
public void parse() throws MessageDescriptorFormatException {
|
|
- currentParserState.start( this );
|
|
+ while ( currentPosition <= originalMessageDescriptor.length() ) {
|
|
+ next();
|
|
+ }
|
|
}
|
|
|
|
public void transitionState(ParserState newState) {
|
|
@@ -122,4 +123,3 @@ public String getOriginalMessageDescriptor() {
|
|
return originalMessageDescriptor;
|
|
}
|
|
}
|
|
-
|
|
diff --git a/engine/src/test/java/org/hibernate/validator/bugs/TooBigMessageTest.java b/engine/src/test/java/org/hibernate/validator/bugs/TooBigMessageTest.java
|
|
new file mode 100644
|
|
index 000000000..4eb8ffc18
|
|
--- /dev/null
|
|
+++ b/engine/src/test/java/org/hibernate/validator/bugs/TooBigMessageTest.java
|
|
@@ -0,0 +1,90 @@
|
|
+/*
|
|
+ * Hibernate Validator, declare and validate application constraints
|
|
+ *
|
|
+ * License: Apache License, Version 2.0
|
|
+ * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
|
|
+ */
|
|
+package org.hibernate.validator.bugs;
|
|
+
|
|
+import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintViolationMessages;
|
|
+import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNumberOfViolations;
|
|
+
|
|
+import java.util.Set;
|
|
+
|
|
+import javax.validation.ConstraintViolation;
|
|
+import javax.validation.Validator;
|
|
+import javax.validation.constraints.NotNull;
|
|
+
|
|
+import org.hibernate.validator.testutil.TestForIssue;
|
|
+import org.hibernate.validator.testutils.ValidatorUtil;
|
|
+import org.junit.Test;
|
|
+
|
|
+/**
|
|
+ * Ensure large error messages can be interpolated.
|
|
+ *
|
|
+ * @author Gunnar Morling
|
|
+ */
|
|
+public class TooBigMessageTest {
|
|
+
|
|
+ /**
|
|
+ * Large enough to trigger a stack overflow with the recursive scheme, assuming default settings
|
|
+ */
|
|
+ private static final String LARGE_MESSAGE =
|
|
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
+ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890";
|
|
+
|
|
+ @Test
|
|
+ @TestForIssue(jiraKey = "HV-1091")
|
|
+ public void largeMessageCanBeInterpolated() {
|
|
+ Validator validator = ValidatorUtil.getValidator();
|
|
+ GoldFish fish = new GoldFish();
|
|
+
|
|
+ Set<ConstraintViolation<GoldFish>> constraintViolations = validator.validate( fish );
|
|
+ assertNumberOfViolations( constraintViolations, 1 );
|
|
+ assertCorrectConstraintViolationMessages( constraintViolations, LARGE_MESSAGE );
|
|
+ }
|
|
+
|
|
+ private static class GoldFish {
|
|
+
|
|
+ @NotNull(message = LARGE_MESSAGE)
|
|
+ String name;
|
|
+ }
|
|
+}
|
|
diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenIteratorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenIteratorTest.java
|
|
index a63e1996a..281f3bccf 100644
|
|
--- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenIteratorTest.java
|
|
+++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenIteratorTest.java
|
|
@@ -6,15 +6,15 @@
|
|
*/
|
|
package org.hibernate.validator.test.internal.engine.messageinterpolation;
|
|
|
|
+import static org.testng.Assert.assertEquals;
|
|
+import static org.testng.Assert.assertFalse;
|
|
+import static org.testng.Assert.assertTrue;
|
|
+
|
|
import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTermType;
|
|
import org.hibernate.validator.internal.engine.messageinterpolation.parser.TokenCollector;
|
|
import org.hibernate.validator.internal.engine.messageinterpolation.parser.TokenIterator;
|
|
import org.testng.annotations.Test;
|
|
|
|
-import static org.testng.Assert.assertEquals;
|
|
-import static org.testng.Assert.assertFalse;
|
|
-import static org.testng.Assert.assertTrue;
|
|
-
|
|
/**
|
|
* Tests for {@code TokenIterator}.
|
|
*
|