From e94d224095d4ea7f9d8ca175af2721eeee6c1d28 Mon Sep 17 00:00:00 2001 From: jbh010204 Date: Fri, 26 Sep 2025 08:27:52 +0900 Subject: [PATCH] Refactor error message formatting in BadJpqlGrammarErrorListener Signed-off-by: jbh010204 --- .../query/BadJpqlGrammarErrorListener.java | 108 ++++++++++++------ 1 file changed, 71 insertions(+), 37 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BadJpqlGrammarErrorListener.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BadJpqlGrammarErrorListener.java index dd3eab6412..47897d25d6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BadJpqlGrammarErrorListener.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BadJpqlGrammarErrorListener.java @@ -57,41 +57,75 @@ public void syntaxError(Recognizer recognizer, Object offendingSymbol, int /** * Rewrite the error message. */ - private static String formatMessage(Object offendingSymbol, int line, int charPositionInLine, String message, - RecognitionException e, String query) { - - String errorText = "At " + line + ":" + charPositionInLine; - - if (offendingSymbol instanceof CommonToken ct) { - - String token = ct.getText(); - if (!ObjectUtils.isEmpty(token)) { - errorText += " and token '" + token + "'"; - } - } - errorText += ", "; - - if (e instanceof NoViableAltException) { - - errorText += message.substring(0, message.indexOf('\'')); - if (query.isEmpty()) { - errorText += "'*' (empty query string)"; - } else { - - List list = query.lines().toList(); - String lineText = list.get(line - 1); - String text = lineText.substring(0, charPositionInLine) + "*" + lineText.substring(charPositionInLine); - errorText += "'" + text + "'"; - } - - } else if (e instanceof InputMismatchException) { - errorText += message.substring(0, message.length() - 1).replace(" expecting {", - ", expecting one of the following tokens: "); - } else { - errorText += message; - } - - return errorText; - } - + private static String formatMessage(Object offendingSymbol, + int line, + int charPositionInLine, + String message, + RecognitionException e, + String query) { + + StringBuilder errorText = new StringBuilder(256) + .append("At ") + .append(line) + .append(":") + .append(charPositionInLine); + + if (offendingSymbol instanceof CommonToken ct) { + String token = ct.getText(); + if (!ObjectUtils.isEmpty(token)) { + errorText.append(" and token '").append(token).append("'"); + } + } + errorText.append(", "); + + if (e instanceof NoViableAltException) { + int idx = message.indexOf('\''); + if (idx >= 0) { + errorText.append(message, 0, idx); + } else { + errorText.append(message); + } + + if (query.isEmpty()) { + errorText.append("'*' (empty query string)"); + } else { + String lineText = getLineAt(query, line); + + StringBuilder lineSb = new StringBuilder(lineText.length() + 1); // 삽입 고려 + lineSb.append(lineText); + if (charPositionInLine >= 0 && charPositionInLine <= lineSb.length()) { + lineSb.insert(charPositionInLine, '*'); + } else { + lineSb.append('*'); + } + + errorText.append("'").append(lineSb).append("'"); + } + + } else if (e instanceof InputMismatchException) { + errorText.append( + message.substring(0, message.length() - 1) + .replace(" expecting {", ", expecting one of the following tokens: ") + ); + } else { + errorText.append(message); + } + + return errorText.toString(); + } + + private static String getLineAt(String query, int line) { + if (query == null || query.isEmpty()) { + return ""; + } + + String[] lines = query.split("\\r?\\n"); + int lineIndex = line - 1; // 라인 번호는 1-based이므로 0-based 인덱스로 변환 + + if (lineIndex >= 0 && lineIndex < lines.length) { + return lines[lineIndex]; + } + + return ""; // 라인 번호가 범위를 벗어나면 빈 문자열 반환 + } }