From 790d50f9108a0438718019486d717c608f65c58f Mon Sep 17 00:00:00 2001 From: aboyko Date: Fri, 16 Aug 2024 08:56:14 -0400 Subject: [PATCH] CRON parser. Syntax highlighting and validation inside java code. --- headless-services/commons/jpql/.gitignore | 22 +- .../commons/jpql/generate-parsers.sh | 1 + .../commons/jpql/grammars/CronLexer.g4 | 71 + .../commons/jpql/grammars/CronParser.g4 | 110 ++ .../ide/vscode/parser/cron/CronLexer.java | 253 +++ .../ide/vscode/parser/cron/CronParser.java | 1470 +++++++++++++++++ .../parser/cron/CronParserBaseListener.java | 256 +++ .../parser/cron/CronParserListener.java | 190 +++ .../spring-boot-language-server/pom.xml | 10 + .../ide/vscode/boot/app/CronConfig.java | 31 + .../ide/vscode/boot/app/JdtConfig.java | 12 + .../boot/common/SpringProblemCategories.java | 5 +- .../ide/vscode/boot/java/Annotations.java | 2 + .../boot/java/cron/CronProblemType.java | 70 + .../vscode/boot/java/cron/CronReconciler.java | 237 +++ .../boot/java/cron/CronSemanticTokens.java | 170 ++ .../boot/java/cron/JdtCronReconciler.java | 74 + .../cron/JdtCronSemanticTokensProvider.java | 85 + .../data/jpa/queries/AntlrReconciler.java | 43 +- .../JdtDataQuerySemanticTokensProvider.java | 10 +- .../jpa/queries/QueryJdtAstReconciler.java | 2 +- .../vscode/boot/java/spel/SpelReconciler.java | 1 + .../src/main/resources/problem-types.json | 30 +- .../boot/java/cron/CronReconcilerTest.java | 75 + .../java/cron/CronSemanticTokensTest.java | 277 ++++ .../boot/java/cron/JdtCronReconcilerTest.java | 135 ++ .../JdtCronSemanticTokensProviderTest.java | 370 +++++ .../vscode/boot/test/ProblemTypesToJson.java | 6 +- .../commons-vscode/package-lock.json | 462 ++++-- .../vscode-spring-boot/package.json | 42 +- 30 files changed, 4310 insertions(+), 212 deletions(-) create mode 100644 headless-services/commons/jpql/grammars/CronLexer.g4 create mode 100644 headless-services/commons/jpql/grammars/CronParser.g4 create mode 100644 headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronLexer.java create mode 100644 headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronParser.java create mode 100644 headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronParserBaseListener.java create mode 100644 headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronParserListener.java create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/CronConfig.java create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/CronProblemType.java create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/CronReconciler.java create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/CronSemanticTokens.java create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/JdtCronReconciler.java create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/JdtCronSemanticTokensProvider.java create mode 100644 headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/CronReconcilerTest.java create mode 100644 headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/CronSemanticTokensTest.java create mode 100644 headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/JdtCronReconcilerTest.java create mode 100644 headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/JdtCronSemanticTokensProviderTest.java diff --git a/headless-services/commons/jpql/.gitignore b/headless-services/commons/jpql/.gitignore index d7812a664..79a5fc0fc 100644 --- a/headless-services/commons/jpql/.gitignore +++ b/headless-services/commons/jpql/.gitignore @@ -1,22 +1,4 @@ /antlr-4.13.1.jar -/src/main/java/org/springframework/ide/vscode/parser/jpql/Jpql.interp -/src/main/java/org/springframework/ide/vscode/parser/jpql/Jpql.tokens -/src/main/java/org/springframework/ide/vscode/parser/jpql/JpqlLexer.interp -/src/main/java/org/springframework/ide/vscode/parser/jpql/JpqlLexer.tokens -/src/main/java/org/springframework/ide/vscode/parser/hql/Hql.interp -/src/main/java/org/springframework/ide/vscode/parser/hql/Hql.tokens -/src/main/java/org/springframework/ide/vscode/parser/hql/HqlLexer.interp -/src/main/java/org/springframework/ide/vscode/parser/hql/HqlLexer.tokens -/src/main/java/org/springframework/ide/vscode/parser/mysql/MySqlParser.interp -/src/main/java/org/springframework/ide/vscode/parser/mysql/MySqlParser.tokens -/src/main/java/org/springframework/ide/vscode/parser/mysql/MySqlLexer.interp -/src/main/java/org/springframework/ide/vscode/parser/mysql/MySqlLexer.tokens -/src/main/java/org/springframework/ide/vscode/parser/postgresql/PostgreSqlParser.interp -/src/main/java/org/springframework/ide/vscode/parser/postgresql/PostgreSqlParser.tokens -/src/main/java/org/springframework/ide/vscode/parser/postgresql/PostgreSqlLexer.interp -/src/main/java/org/springframework/ide/vscode/parser/postgresql/PostgreSqlLexer.tokens -/src/main/java/org/springframework/ide/vscode/parser/spel/SpelParser.interp -/src/main/java/org/springframework/ide/vscode/parser/spel/SpelParser.tokens -/src/main/java/org/springframework/ide/vscode/parser/spel/SpelLexer.interp -/src/main/java/org/springframework/ide/vscode/parser/spel/SpelLexer.tokens +/src/main/java/org/springframework/ide/vscode/**/*.interp +/src/main/java/org/springframework/ide/vscode/**/*.tokens /target diff --git a/headless-services/commons/jpql/generate-parsers.sh b/headless-services/commons/jpql/generate-parsers.sh index e44a3044b..2ee29a541 100755 --- a/headless-services/commons/jpql/generate-parsers.sh +++ b/headless-services/commons/jpql/generate-parsers.sh @@ -11,4 +11,5 @@ java -jar ${workdir}/antlr-${ANTLR_VERSION}.jar Hql.g4 -o ${workdir}/src/main/ja java -jar ${workdir}/antlr-${ANTLR_VERSION}.jar MySqlLexer.g4 MySqlParser.g4 -o ${workdir}/src/main/java/org/springframework/ide/vscode/parser/mysql -package org.springframework.ide.vscode.parser.mysql java -jar ${workdir}/antlr-${ANTLR_VERSION}.jar SpelLexer.g4 SpelParser.g4 -o ${workdir}/src/main/java/org/springframework/ide/vscode/parser/spel -package org.springframework.ide.vscode.parser.spel java -jar ${workdir}/antlr-${ANTLR_VERSION}.jar PostgreSqlLexer.g4 PostgreSqlParser.g4 -o ${workdir}/src/main/java/org/springframework/ide/vscode/parser/postgresql -package org.springframework.ide.vscode.parser.postgresql +java -jar ${workdir}/antlr-${ANTLR_VERSION}.jar CronLexer.g4 CronParser.g4 -o ${workdir}/src/main/java/org/springframework/ide/vscode/parser/cron -package org.springframework.ide.vscode.parser.cron diff --git a/headless-services/commons/jpql/grammars/CronLexer.g4 b/headless-services/commons/jpql/grammars/CronLexer.g4 new file mode 100644 index 000000000..d59f5065a --- /dev/null +++ b/headless-services/commons/jpql/grammars/CronLexer.g4 @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2024 Broadcom, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + *******************************************************************************/ + +lexer grammar CronLexer; + +WS + : [ \t\r\n]+ + ; + +STAR : '*'; +QUESTION : '?'; +DASH : '-'; +SLASH : '/'; +COMMA : ','; +L : 'L'; +W : 'W'; +LW : 'LW'; +TAG : '#'; + +INT + : DIGIT+ + ; + +MON: 'MON'; +TUE: 'TUE'; +WED: 'WED'; +THU: 'THU'; +FRI: 'FRI'; +SAT: 'SAT'; +SUN: 'SUN'; + +JAN: 'JAN'; +FEB: 'FEB'; +MAR: 'MAR'; +APR: 'APR'; +MAY: 'MAY'; +JUN: 'JUN'; +JUL: 'JUL'; +AUG: 'AUG'; +SEP: 'SEP'; +OCT: 'OCT'; +NOV: 'NOV'; +DEC: 'DEC'; + +IDENTIFIER + : (ALPHABETIC | '_' )* (ALPHABETIC_NO_L | '_') + ; + +MACRO + : '@' (ALPHABETIC | '_') (ALPHABETIC | DIGIT | '_' | '$')* + ; + +fragment ALPHABETIC + : [a-zA-Z] + ; + +fragment ALPHABETIC_NO_L + : [a-zA-KM-Z] + ; + +fragment DIGIT + : [0-9] + ; \ No newline at end of file diff --git a/headless-services/commons/jpql/grammars/CronParser.g4 b/headless-services/commons/jpql/grammars/CronParser.g4 new file mode 100644 index 000000000..093c41769 --- /dev/null +++ b/headless-services/commons/jpql/grammars/CronParser.g4 @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2024 Broadcom, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + *******************************************************************************/ +parser grammar CronParser; +options { tokenVocab=CronLexer; } + +cronExpression + : WS* secondsElement WS+ minutesElement WS+ hoursElement WS+ daysElement WS+ monthsElement WS+ daysOfWeekElement WS* + | WS* MACRO WS* + ; + +secondsElement + : cronElement + ; + +minutesElement + : cronElement + ; + +hoursElement + : cronElement + ; + +daysElement + : nearestWeekDayToDayOfMonthElement + | lastDayOfMonthElement + | cronElement + ; + +monthsElement + : cronElement + ; + +daysOfWeekElement + : nthDayOfWeekElement + | lastDayOfWeekElement + | cronElement + ; + +cronElement + : rangeCronList + | periodicCronElement + ; + +rangeCronElement + : terminalCronElement (DASH (INT | weekdayLiteral | monthLiteral))* + ; + +terminalCronElement + : ( INT | weekdayLiteral | monthLiteral | STAR | QUESTION ) + ; + +periodicCronElement + : terminalCronElement SLASH rangeCronList + ; + +rangeCronList + : rangeCronElement (COMMA rangeCronElement)* + ; + +nthDayOfWeekElement + : INT TAG INT + | weekdayLiteral TAG INT + ; + +lastDayOfWeekElement + : INT L + | weekdayLiteral L + ; + +nearestWeekDayToDayOfMonthElement + : INT W + ; + +lastDayOfMonthElement + : L (DASH INT)? + | LW + ; + +weekdayLiteral + : MON + | TUE + | WED + | THU + | FRI + | SAT + | SUN + ; + +monthLiteral + : JAN + | FEB + | MAR + | APR + | MAY + | JUN + | JUL + | AUG + | SEP + | OCT + | NOV + | DEC + ; diff --git a/headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronLexer.java b/headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronLexer.java new file mode 100644 index 000000000..4a0ecabe8 --- /dev/null +++ b/headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronLexer.java @@ -0,0 +1,253 @@ +// Generated from CronLexer.g4 by ANTLR 4.13.1 +package org.springframework.ide.vscode.parser.cron; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.misc.*; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"}) +public class CronLexer extends Lexer { + static { RuntimeMetaData.checkVersion("4.13.1", RuntimeMetaData.VERSION); } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + WS=1, STAR=2, QUESTION=3, DASH=4, SLASH=5, COMMA=6, L=7, W=8, LW=9, TAG=10, + INT=11, MON=12, TUE=13, WED=14, THU=15, FRI=16, SAT=17, SUN=18, JAN=19, + FEB=20, MAR=21, APR=22, MAY=23, JUN=24, JUL=25, AUG=26, SEP=27, OCT=28, + NOV=29, DEC=30, IDENTIFIER=31, MACRO=32; + public static String[] channelNames = { + "DEFAULT_TOKEN_CHANNEL", "HIDDEN" + }; + + public static String[] modeNames = { + "DEFAULT_MODE" + }; + + private static String[] makeRuleNames() { + return new String[] { + "WS", "STAR", "QUESTION", "DASH", "SLASH", "COMMA", "L", "W", "LW", "TAG", + "INT", "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN", "JAN", "FEB", + "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC", + "IDENTIFIER", "MACRO", "ALPHABETIC", "ALPHABETIC_NO_L", "DIGIT" + }; + } + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[] { + null, null, "'*'", "'?'", "'-'", "'/'", "','", "'L'", "'W'", "'LW'", + "'#'", null, "'MON'", "'TUE'", "'WED'", "'THU'", "'FRI'", "'SAT'", "'SUN'", + "'JAN'", "'FEB'", "'MAR'", "'APR'", "'MAY'", "'JUN'", "'JUL'", "'AUG'", + "'SEP'", "'OCT'", "'NOV'", "'DEC'" + }; + } + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + private static String[] makeSymbolicNames() { + return new String[] { + null, "WS", "STAR", "QUESTION", "DASH", "SLASH", "COMMA", "L", "W", "LW", + "TAG", "INT", "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN", "JAN", + "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", + "DEC", "IDENTIFIER", "MACRO" + }; + } + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + + public CronLexer(CharStream input) { + super(input); + _interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); + } + + @Override + public String getGrammarFileName() { return "CronLexer.g4"; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public String[] getChannelNames() { return channelNames; } + + @Override + public String[] getModeNames() { return modeNames; } + + @Override + public ATN getATN() { return _ATN; } + + public static final String _serializedATN = + "\u0004\u0000 \u00ce\u0006\uffff\uffff\u0002\u0000\u0007\u0000\u0002\u0001"+ + "\u0007\u0001\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004"+ + "\u0007\u0004\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007"+ + "\u0007\u0007\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b"+ + "\u0007\u000b\u0002\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002"+ + "\u000f\u0007\u000f\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0002"+ + "\u0012\u0007\u0012\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014\u0002"+ + "\u0015\u0007\u0015\u0002\u0016\u0007\u0016\u0002\u0017\u0007\u0017\u0002"+ + "\u0018\u0007\u0018\u0002\u0019\u0007\u0019\u0002\u001a\u0007\u001a\u0002"+ + "\u001b\u0007\u001b\u0002\u001c\u0007\u001c\u0002\u001d\u0007\u001d\u0002"+ + "\u001e\u0007\u001e\u0002\u001f\u0007\u001f\u0002 \u0007 \u0002!\u0007"+ + "!\u0002\"\u0007\"\u0001\u0000\u0004\u0000I\b\u0000\u000b\u0000\f\u0000"+ + "J\u0001\u0001\u0001\u0001\u0001\u0002\u0001\u0002\u0001\u0003\u0001\u0003"+ + "\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005\u0001\u0006\u0001\u0006"+ + "\u0001\u0007\u0001\u0007\u0001\b\u0001\b\u0001\b\u0001\t\u0001\t\u0001"+ + "\n\u0004\na\b\n\u000b\n\f\nb\u0001\u000b\u0001\u000b\u0001\u000b\u0001"+ + "\u000b\u0001\f\u0001\f\u0001\f\u0001\f\u0001\r\u0001\r\u0001\r\u0001\r"+ + "\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000f\u0001\u000f"+ + "\u0001\u000f\u0001\u000f\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010"+ + "\u0001\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001\u0012\u0001\u0012"+ + "\u0001\u0012\u0001\u0012\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013"+ + "\u0001\u0014\u0001\u0014\u0001\u0014\u0001\u0014\u0001\u0015\u0001\u0015"+ + "\u0001\u0015\u0001\u0015\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016"+ + "\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0018\u0001\u0018"+ + "\u0001\u0018\u0001\u0018\u0001\u0019\u0001\u0019\u0001\u0019\u0001\u0019"+ + "\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001b\u0001\u001b"+ + "\u0001\u001b\u0001\u001b\u0001\u001c\u0001\u001c\u0001\u001c\u0001\u001c"+ + "\u0001\u001d\u0001\u001d\u0001\u001d\u0001\u001d\u0001\u001e\u0001\u001e"+ + "\u0005\u001e\u00b3\b\u001e\n\u001e\f\u001e\u00b6\t\u001e\u0001\u001e\u0001"+ + "\u001e\u0003\u001e\u00ba\b\u001e\u0001\u001f\u0001\u001f\u0001\u001f\u0003"+ + "\u001f\u00bf\b\u001f\u0001\u001f\u0001\u001f\u0001\u001f\u0005\u001f\u00c4"+ + "\b\u001f\n\u001f\f\u001f\u00c7\t\u001f\u0001 \u0001 \u0001!\u0001!\u0001"+ + "\"\u0001\"\u0000\u0000#\u0001\u0001\u0003\u0002\u0005\u0003\u0007\u0004"+ + "\t\u0005\u000b\u0006\r\u0007\u000f\b\u0011\t\u0013\n\u0015\u000b\u0017"+ + "\f\u0019\r\u001b\u000e\u001d\u000f\u001f\u0010!\u0011#\u0012%\u0013\'"+ + "\u0014)\u0015+\u0016-\u0017/\u00181\u00193\u001a5\u001b7\u001c9\u001d"+ + ";\u001e=\u001f? A\u0000C\u0000E\u0000\u0001\u0000\u0005\u0003\u0000\t"+ + "\n\r\r \u0002\u0000$$__\u0002\u0000AZaz\u0003\u0000AKMZaz\u0001\u0000"+ + "09\u00d3\u0000\u0001\u0001\u0000\u0000\u0000\u0000\u0003\u0001\u0000\u0000"+ + "\u0000\u0000\u0005\u0001\u0000\u0000\u0000\u0000\u0007\u0001\u0000\u0000"+ + "\u0000\u0000\t\u0001\u0000\u0000\u0000\u0000\u000b\u0001\u0000\u0000\u0000"+ + "\u0000\r\u0001\u0000\u0000\u0000\u0000\u000f\u0001\u0000\u0000\u0000\u0000"+ + "\u0011\u0001\u0000\u0000\u0000\u0000\u0013\u0001\u0000\u0000\u0000\u0000"+ + "\u0015\u0001\u0000\u0000\u0000\u0000\u0017\u0001\u0000\u0000\u0000\u0000"+ + "\u0019\u0001\u0000\u0000\u0000\u0000\u001b\u0001\u0000\u0000\u0000\u0000"+ + "\u001d\u0001\u0000\u0000\u0000\u0000\u001f\u0001\u0000\u0000\u0000\u0000"+ + "!\u0001\u0000\u0000\u0000\u0000#\u0001\u0000\u0000\u0000\u0000%\u0001"+ + "\u0000\u0000\u0000\u0000\'\u0001\u0000\u0000\u0000\u0000)\u0001\u0000"+ + "\u0000\u0000\u0000+\u0001\u0000\u0000\u0000\u0000-\u0001\u0000\u0000\u0000"+ + "\u0000/\u0001\u0000\u0000\u0000\u00001\u0001\u0000\u0000\u0000\u00003"+ + "\u0001\u0000\u0000\u0000\u00005\u0001\u0000\u0000\u0000\u00007\u0001\u0000"+ + "\u0000\u0000\u00009\u0001\u0000\u0000\u0000\u0000;\u0001\u0000\u0000\u0000"+ + "\u0000=\u0001\u0000\u0000\u0000\u0000?\u0001\u0000\u0000\u0000\u0001H"+ + "\u0001\u0000\u0000\u0000\u0003L\u0001\u0000\u0000\u0000\u0005N\u0001\u0000"+ + "\u0000\u0000\u0007P\u0001\u0000\u0000\u0000\tR\u0001\u0000\u0000\u0000"+ + "\u000bT\u0001\u0000\u0000\u0000\rV\u0001\u0000\u0000\u0000\u000fX\u0001"+ + "\u0000\u0000\u0000\u0011Z\u0001\u0000\u0000\u0000\u0013]\u0001\u0000\u0000"+ + "\u0000\u0015`\u0001\u0000\u0000\u0000\u0017d\u0001\u0000\u0000\u0000\u0019"+ + "h\u0001\u0000\u0000\u0000\u001bl\u0001\u0000\u0000\u0000\u001dp\u0001"+ + "\u0000\u0000\u0000\u001ft\u0001\u0000\u0000\u0000!x\u0001\u0000\u0000"+ + "\u0000#|\u0001\u0000\u0000\u0000%\u0080\u0001\u0000\u0000\u0000\'\u0084"+ + "\u0001\u0000\u0000\u0000)\u0088\u0001\u0000\u0000\u0000+\u008c\u0001\u0000"+ + "\u0000\u0000-\u0090\u0001\u0000\u0000\u0000/\u0094\u0001\u0000\u0000\u0000"+ + "1\u0098\u0001\u0000\u0000\u00003\u009c\u0001\u0000\u0000\u00005\u00a0"+ + "\u0001\u0000\u0000\u00007\u00a4\u0001\u0000\u0000\u00009\u00a8\u0001\u0000"+ + "\u0000\u0000;\u00ac\u0001\u0000\u0000\u0000=\u00b4\u0001\u0000\u0000\u0000"+ + "?\u00bb\u0001\u0000\u0000\u0000A\u00c8\u0001\u0000\u0000\u0000C\u00ca"+ + "\u0001\u0000\u0000\u0000E\u00cc\u0001\u0000\u0000\u0000GI\u0007\u0000"+ + "\u0000\u0000HG\u0001\u0000\u0000\u0000IJ\u0001\u0000\u0000\u0000JH\u0001"+ + "\u0000\u0000\u0000JK\u0001\u0000\u0000\u0000K\u0002\u0001\u0000\u0000"+ + "\u0000LM\u0005*\u0000\u0000M\u0004\u0001\u0000\u0000\u0000NO\u0005?\u0000"+ + "\u0000O\u0006\u0001\u0000\u0000\u0000PQ\u0005-\u0000\u0000Q\b\u0001\u0000"+ + "\u0000\u0000RS\u0005/\u0000\u0000S\n\u0001\u0000\u0000\u0000TU\u0005,"+ + "\u0000\u0000U\f\u0001\u0000\u0000\u0000VW\u0005L\u0000\u0000W\u000e\u0001"+ + "\u0000\u0000\u0000XY\u0005W\u0000\u0000Y\u0010\u0001\u0000\u0000\u0000"+ + "Z[\u0005L\u0000\u0000[\\\u0005W\u0000\u0000\\\u0012\u0001\u0000\u0000"+ + "\u0000]^\u0005#\u0000\u0000^\u0014\u0001\u0000\u0000\u0000_a\u0003E\""+ + "\u0000`_\u0001\u0000\u0000\u0000ab\u0001\u0000\u0000\u0000b`\u0001\u0000"+ + "\u0000\u0000bc\u0001\u0000\u0000\u0000c\u0016\u0001\u0000\u0000\u0000"+ + "de\u0005M\u0000\u0000ef\u0005O\u0000\u0000fg\u0005N\u0000\u0000g\u0018"+ + "\u0001\u0000\u0000\u0000hi\u0005T\u0000\u0000ij\u0005U\u0000\u0000jk\u0005"+ + "E\u0000\u0000k\u001a\u0001\u0000\u0000\u0000lm\u0005W\u0000\u0000mn\u0005"+ + "E\u0000\u0000no\u0005D\u0000\u0000o\u001c\u0001\u0000\u0000\u0000pq\u0005"+ + "T\u0000\u0000qr\u0005H\u0000\u0000rs\u0005U\u0000\u0000s\u001e\u0001\u0000"+ + "\u0000\u0000tu\u0005F\u0000\u0000uv\u0005R\u0000\u0000vw\u0005I\u0000"+ + "\u0000w \u0001\u0000\u0000\u0000xy\u0005S\u0000\u0000yz\u0005A\u0000\u0000"+ + "z{\u0005T\u0000\u0000{\"\u0001\u0000\u0000\u0000|}\u0005S\u0000\u0000"+ + "}~\u0005U\u0000\u0000~\u007f\u0005N\u0000\u0000\u007f$\u0001\u0000\u0000"+ + "\u0000\u0080\u0081\u0005J\u0000\u0000\u0081\u0082\u0005A\u0000\u0000\u0082"+ + "\u0083\u0005N\u0000\u0000\u0083&\u0001\u0000\u0000\u0000\u0084\u0085\u0005"+ + "F\u0000\u0000\u0085\u0086\u0005E\u0000\u0000\u0086\u0087\u0005B\u0000"+ + "\u0000\u0087(\u0001\u0000\u0000\u0000\u0088\u0089\u0005M\u0000\u0000\u0089"+ + "\u008a\u0005A\u0000\u0000\u008a\u008b\u0005R\u0000\u0000\u008b*\u0001"+ + "\u0000\u0000\u0000\u008c\u008d\u0005A\u0000\u0000\u008d\u008e\u0005P\u0000"+ + "\u0000\u008e\u008f\u0005R\u0000\u0000\u008f,\u0001\u0000\u0000\u0000\u0090"+ + "\u0091\u0005M\u0000\u0000\u0091\u0092\u0005A\u0000\u0000\u0092\u0093\u0005"+ + "Y\u0000\u0000\u0093.\u0001\u0000\u0000\u0000\u0094\u0095\u0005J\u0000"+ + "\u0000\u0095\u0096\u0005U\u0000\u0000\u0096\u0097\u0005N\u0000\u0000\u0097"+ + "0\u0001\u0000\u0000\u0000\u0098\u0099\u0005J\u0000\u0000\u0099\u009a\u0005"+ + "U\u0000\u0000\u009a\u009b\u0005L\u0000\u0000\u009b2\u0001\u0000\u0000"+ + "\u0000\u009c\u009d\u0005A\u0000\u0000\u009d\u009e\u0005U\u0000\u0000\u009e"+ + "\u009f\u0005G\u0000\u0000\u009f4\u0001\u0000\u0000\u0000\u00a0\u00a1\u0005"+ + "S\u0000\u0000\u00a1\u00a2\u0005E\u0000\u0000\u00a2\u00a3\u0005P\u0000"+ + "\u0000\u00a36\u0001\u0000\u0000\u0000\u00a4\u00a5\u0005O\u0000\u0000\u00a5"+ + "\u00a6\u0005C\u0000\u0000\u00a6\u00a7\u0005T\u0000\u0000\u00a78\u0001"+ + "\u0000\u0000\u0000\u00a8\u00a9\u0005N\u0000\u0000\u00a9\u00aa\u0005O\u0000"+ + "\u0000\u00aa\u00ab\u0005V\u0000\u0000\u00ab:\u0001\u0000\u0000\u0000\u00ac"+ + "\u00ad\u0005D\u0000\u0000\u00ad\u00ae\u0005E\u0000\u0000\u00ae\u00af\u0005"+ + "C\u0000\u0000\u00af<\u0001\u0000\u0000\u0000\u00b0\u00b3\u0003A \u0000"+ + "\u00b1\u00b3\u0005_\u0000\u0000\u00b2\u00b0\u0001\u0000\u0000\u0000\u00b2"+ + "\u00b1\u0001\u0000\u0000\u0000\u00b3\u00b6\u0001\u0000\u0000\u0000\u00b4"+ + "\u00b2\u0001\u0000\u0000\u0000\u00b4\u00b5\u0001\u0000\u0000\u0000\u00b5"+ + "\u00b9\u0001\u0000\u0000\u0000\u00b6\u00b4\u0001\u0000\u0000\u0000\u00b7"+ + "\u00ba\u0003C!\u0000\u00b8\u00ba\u0005_\u0000\u0000\u00b9\u00b7\u0001"+ + "\u0000\u0000\u0000\u00b9\u00b8\u0001\u0000\u0000\u0000\u00ba>\u0001\u0000"+ + "\u0000\u0000\u00bb\u00be\u0005@\u0000\u0000\u00bc\u00bf\u0003A \u0000"+ + "\u00bd\u00bf\u0005_\u0000\u0000\u00be\u00bc\u0001\u0000\u0000\u0000\u00be"+ + "\u00bd\u0001\u0000\u0000\u0000\u00bf\u00c5\u0001\u0000\u0000\u0000\u00c0"+ + "\u00c4\u0003A \u0000\u00c1\u00c4\u0003E\"\u0000\u00c2\u00c4\u0007\u0001"+ + "\u0000\u0000\u00c3\u00c0\u0001\u0000\u0000\u0000\u00c3\u00c1\u0001\u0000"+ + "\u0000\u0000\u00c3\u00c2\u0001\u0000\u0000\u0000\u00c4\u00c7\u0001\u0000"+ + "\u0000\u0000\u00c5\u00c3\u0001\u0000\u0000\u0000\u00c5\u00c6\u0001\u0000"+ + "\u0000\u0000\u00c6@\u0001\u0000\u0000\u0000\u00c7\u00c5\u0001\u0000\u0000"+ + "\u0000\u00c8\u00c9\u0007\u0002\u0000\u0000\u00c9B\u0001\u0000\u0000\u0000"+ + "\u00ca\u00cb\u0007\u0003\u0000\u0000\u00cbD\u0001\u0000\u0000\u0000\u00cc"+ + "\u00cd\u0007\u0004\u0000\u0000\u00cdF\u0001\u0000\u0000\u0000\t\u0000"+ + "Jb\u00b2\u00b4\u00b9\u00be\u00c3\u00c5\u0000"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; + for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { + _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); + } + } +} \ No newline at end of file diff --git a/headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronParser.java b/headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronParser.java new file mode 100644 index 000000000..4df375810 --- /dev/null +++ b/headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronParser.java @@ -0,0 +1,1470 @@ +// Generated from CronParser.g4 by ANTLR 4.13.1 +package org.springframework.ide.vscode.parser.cron; +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.misc.*; +import org.antlr.v4.runtime.tree.*; +import java.util.List; +import java.util.Iterator; +import java.util.ArrayList; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue"}) +public class CronParser extends Parser { + static { RuntimeMetaData.checkVersion("4.13.1", RuntimeMetaData.VERSION); } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + WS=1, STAR=2, QUESTION=3, DASH=4, SLASH=5, COMMA=6, L=7, W=8, LW=9, TAG=10, + INT=11, MON=12, TUE=13, WED=14, THU=15, FRI=16, SAT=17, SUN=18, JAN=19, + FEB=20, MAR=21, APR=22, MAY=23, JUN=24, JUL=25, AUG=26, SEP=27, OCT=28, + NOV=29, DEC=30, IDENTIFIER=31, MACRO=32; + public static final int + RULE_cronExpression = 0, RULE_secondsElement = 1, RULE_minutesElement = 2, + RULE_hoursElement = 3, RULE_daysElement = 4, RULE_monthsElement = 5, RULE_daysOfWeekElement = 6, + RULE_cronElement = 7, RULE_rangeCronElement = 8, RULE_terminalCronElement = 9, + RULE_periodicCronElement = 10, RULE_rangeCronList = 11, RULE_nthDayOfWeekElement = 12, + RULE_lastDayOfWeekElement = 13, RULE_nearestWeekDayToDayOfMonthElement = 14, + RULE_lastDayOfMonthElement = 15, RULE_weekdayLiteral = 16, RULE_monthLiteral = 17; + private static String[] makeRuleNames() { + return new String[] { + "cronExpression", "secondsElement", "minutesElement", "hoursElement", + "daysElement", "monthsElement", "daysOfWeekElement", "cronElement", "rangeCronElement", + "terminalCronElement", "periodicCronElement", "rangeCronList", "nthDayOfWeekElement", + "lastDayOfWeekElement", "nearestWeekDayToDayOfMonthElement", "lastDayOfMonthElement", + "weekdayLiteral", "monthLiteral" + }; + } + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[] { + null, null, "'*'", "'?'", "'-'", "'/'", "','", "'L'", "'W'", "'LW'", + "'#'", null, "'MON'", "'TUE'", "'WED'", "'THU'", "'FRI'", "'SAT'", "'SUN'", + "'JAN'", "'FEB'", "'MAR'", "'APR'", "'MAY'", "'JUN'", "'JUL'", "'AUG'", + "'SEP'", "'OCT'", "'NOV'", "'DEC'" + }; + } + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + private static String[] makeSymbolicNames() { + return new String[] { + null, "WS", "STAR", "QUESTION", "DASH", "SLASH", "COMMA", "L", "W", "LW", + "TAG", "INT", "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN", "JAN", + "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", + "DEC", "IDENTIFIER", "MACRO" + }; + } + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + @Override + public String getGrammarFileName() { return "CronParser.g4"; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public ATN getATN() { return _ATN; } + + public CronParser(TokenStream input) { + super(input); + _interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); + } + + @SuppressWarnings("CheckReturnValue") + public static class CronExpressionContext extends ParserRuleContext { + public SecondsElementContext secondsElement() { + return getRuleContext(SecondsElementContext.class,0); + } + public MinutesElementContext minutesElement() { + return getRuleContext(MinutesElementContext.class,0); + } + public HoursElementContext hoursElement() { + return getRuleContext(HoursElementContext.class,0); + } + public DaysElementContext daysElement() { + return getRuleContext(DaysElementContext.class,0); + } + public MonthsElementContext monthsElement() { + return getRuleContext(MonthsElementContext.class,0); + } + public DaysOfWeekElementContext daysOfWeekElement() { + return getRuleContext(DaysOfWeekElementContext.class,0); + } + public List WS() { return getTokens(CronParser.WS); } + public TerminalNode WS(int i) { + return getToken(CronParser.WS, i); + } + public TerminalNode MACRO() { return getToken(CronParser.MACRO, 0); } + public CronExpressionContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_cronExpression; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterCronExpression(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitCronExpression(this); + } + } + + public final CronExpressionContext cronExpression() throws RecognitionException { + CronExpressionContext _localctx = new CronExpressionContext(_ctx, getState()); + enterRule(_localctx, 0, RULE_cronExpression); + int _la; + try { + setState(92); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,9,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(39); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==WS) { + { + { + setState(36); + match(WS); + } + } + setState(41); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(42); + secondsElement(); + setState(44); + _errHandler.sync(this); + _la = _input.LA(1); + do { + { + { + setState(43); + match(WS); + } + } + setState(46); + _errHandler.sync(this); + _la = _input.LA(1); + } while ( _la==WS ); + setState(48); + minutesElement(); + setState(50); + _errHandler.sync(this); + _la = _input.LA(1); + do { + { + { + setState(49); + match(WS); + } + } + setState(52); + _errHandler.sync(this); + _la = _input.LA(1); + } while ( _la==WS ); + setState(54); + hoursElement(); + setState(56); + _errHandler.sync(this); + _la = _input.LA(1); + do { + { + { + setState(55); + match(WS); + } + } + setState(58); + _errHandler.sync(this); + _la = _input.LA(1); + } while ( _la==WS ); + setState(60); + daysElement(); + setState(62); + _errHandler.sync(this); + _la = _input.LA(1); + do { + { + { + setState(61); + match(WS); + } + } + setState(64); + _errHandler.sync(this); + _la = _input.LA(1); + } while ( _la==WS ); + setState(66); + monthsElement(); + setState(68); + _errHandler.sync(this); + _la = _input.LA(1); + do { + { + { + setState(67); + match(WS); + } + } + setState(70); + _errHandler.sync(this); + _la = _input.LA(1); + } while ( _la==WS ); + setState(72); + daysOfWeekElement(); + setState(76); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==WS) { + { + { + setState(73); + match(WS); + } + } + setState(78); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(82); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==WS) { + { + { + setState(79); + match(WS); + } + } + setState(84); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(85); + match(MACRO); + setState(89); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==WS) { + { + { + setState(86); + match(WS); + } + } + setState(91); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class SecondsElementContext extends ParserRuleContext { + public CronElementContext cronElement() { + return getRuleContext(CronElementContext.class,0); + } + public SecondsElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_secondsElement; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterSecondsElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitSecondsElement(this); + } + } + + public final SecondsElementContext secondsElement() throws RecognitionException { + SecondsElementContext _localctx = new SecondsElementContext(_ctx, getState()); + enterRule(_localctx, 2, RULE_secondsElement); + try { + enterOuterAlt(_localctx, 1); + { + setState(94); + cronElement(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class MinutesElementContext extends ParserRuleContext { + public CronElementContext cronElement() { + return getRuleContext(CronElementContext.class,0); + } + public MinutesElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_minutesElement; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterMinutesElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitMinutesElement(this); + } + } + + public final MinutesElementContext minutesElement() throws RecognitionException { + MinutesElementContext _localctx = new MinutesElementContext(_ctx, getState()); + enterRule(_localctx, 4, RULE_minutesElement); + try { + enterOuterAlt(_localctx, 1); + { + setState(96); + cronElement(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class HoursElementContext extends ParserRuleContext { + public CronElementContext cronElement() { + return getRuleContext(CronElementContext.class,0); + } + public HoursElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_hoursElement; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterHoursElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitHoursElement(this); + } + } + + public final HoursElementContext hoursElement() throws RecognitionException { + HoursElementContext _localctx = new HoursElementContext(_ctx, getState()); + enterRule(_localctx, 6, RULE_hoursElement); + try { + enterOuterAlt(_localctx, 1); + { + setState(98); + cronElement(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class DaysElementContext extends ParserRuleContext { + public NearestWeekDayToDayOfMonthElementContext nearestWeekDayToDayOfMonthElement() { + return getRuleContext(NearestWeekDayToDayOfMonthElementContext.class,0); + } + public LastDayOfMonthElementContext lastDayOfMonthElement() { + return getRuleContext(LastDayOfMonthElementContext.class,0); + } + public CronElementContext cronElement() { + return getRuleContext(CronElementContext.class,0); + } + public DaysElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_daysElement; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterDaysElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitDaysElement(this); + } + } + + public final DaysElementContext daysElement() throws RecognitionException { + DaysElementContext _localctx = new DaysElementContext(_ctx, getState()); + enterRule(_localctx, 8, RULE_daysElement); + try { + setState(103); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,10,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(100); + nearestWeekDayToDayOfMonthElement(); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(101); + lastDayOfMonthElement(); + } + break; + case 3: + enterOuterAlt(_localctx, 3); + { + setState(102); + cronElement(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class MonthsElementContext extends ParserRuleContext { + public CronElementContext cronElement() { + return getRuleContext(CronElementContext.class,0); + } + public MonthsElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_monthsElement; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterMonthsElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitMonthsElement(this); + } + } + + public final MonthsElementContext monthsElement() throws RecognitionException { + MonthsElementContext _localctx = new MonthsElementContext(_ctx, getState()); + enterRule(_localctx, 10, RULE_monthsElement); + try { + enterOuterAlt(_localctx, 1); + { + setState(105); + cronElement(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class DaysOfWeekElementContext extends ParserRuleContext { + public NthDayOfWeekElementContext nthDayOfWeekElement() { + return getRuleContext(NthDayOfWeekElementContext.class,0); + } + public LastDayOfWeekElementContext lastDayOfWeekElement() { + return getRuleContext(LastDayOfWeekElementContext.class,0); + } + public CronElementContext cronElement() { + return getRuleContext(CronElementContext.class,0); + } + public DaysOfWeekElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_daysOfWeekElement; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterDaysOfWeekElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitDaysOfWeekElement(this); + } + } + + public final DaysOfWeekElementContext daysOfWeekElement() throws RecognitionException { + DaysOfWeekElementContext _localctx = new DaysOfWeekElementContext(_ctx, getState()); + enterRule(_localctx, 12, RULE_daysOfWeekElement); + try { + setState(110); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,11,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(107); + nthDayOfWeekElement(); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(108); + lastDayOfWeekElement(); + } + break; + case 3: + enterOuterAlt(_localctx, 3); + { + setState(109); + cronElement(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class CronElementContext extends ParserRuleContext { + public RangeCronListContext rangeCronList() { + return getRuleContext(RangeCronListContext.class,0); + } + public PeriodicCronElementContext periodicCronElement() { + return getRuleContext(PeriodicCronElementContext.class,0); + } + public CronElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_cronElement; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterCronElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitCronElement(this); + } + } + + public final CronElementContext cronElement() throws RecognitionException { + CronElementContext _localctx = new CronElementContext(_ctx, getState()); + enterRule(_localctx, 14, RULE_cronElement); + try { + setState(114); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,12,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(112); + rangeCronList(); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(113); + periodicCronElement(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class RangeCronElementContext extends ParserRuleContext { + public TerminalCronElementContext terminalCronElement() { + return getRuleContext(TerminalCronElementContext.class,0); + } + public List DASH() { return getTokens(CronParser.DASH); } + public TerminalNode DASH(int i) { + return getToken(CronParser.DASH, i); + } + public List INT() { return getTokens(CronParser.INT); } + public TerminalNode INT(int i) { + return getToken(CronParser.INT, i); + } + public List weekdayLiteral() { + return getRuleContexts(WeekdayLiteralContext.class); + } + public WeekdayLiteralContext weekdayLiteral(int i) { + return getRuleContext(WeekdayLiteralContext.class,i); + } + public List monthLiteral() { + return getRuleContexts(MonthLiteralContext.class); + } + public MonthLiteralContext monthLiteral(int i) { + return getRuleContext(MonthLiteralContext.class,i); + } + public RangeCronElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_rangeCronElement; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterRangeCronElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitRangeCronElement(this); + } + } + + public final RangeCronElementContext rangeCronElement() throws RecognitionException { + RangeCronElementContext _localctx = new RangeCronElementContext(_ctx, getState()); + enterRule(_localctx, 16, RULE_rangeCronElement); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(116); + terminalCronElement(); + setState(125); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==DASH) { + { + { + setState(117); + match(DASH); + setState(121); + _errHandler.sync(this); + switch (_input.LA(1)) { + case INT: + { + setState(118); + match(INT); + } + break; + case MON: + case TUE: + case WED: + case THU: + case FRI: + case SAT: + case SUN: + { + setState(119); + weekdayLiteral(); + } + break; + case JAN: + case FEB: + case MAR: + case APR: + case MAY: + case JUN: + case JUL: + case AUG: + case SEP: + case OCT: + case NOV: + case DEC: + { + setState(120); + monthLiteral(); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + setState(127); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class TerminalCronElementContext extends ParserRuleContext { + public TerminalNode INT() { return getToken(CronParser.INT, 0); } + public WeekdayLiteralContext weekdayLiteral() { + return getRuleContext(WeekdayLiteralContext.class,0); + } + public MonthLiteralContext monthLiteral() { + return getRuleContext(MonthLiteralContext.class,0); + } + public TerminalNode STAR() { return getToken(CronParser.STAR, 0); } + public TerminalNode QUESTION() { return getToken(CronParser.QUESTION, 0); } + public TerminalCronElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_terminalCronElement; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterTerminalCronElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitTerminalCronElement(this); + } + } + + public final TerminalCronElementContext terminalCronElement() throws RecognitionException { + TerminalCronElementContext _localctx = new TerminalCronElementContext(_ctx, getState()); + enterRule(_localctx, 18, RULE_terminalCronElement); + try { + enterOuterAlt(_localctx, 1); + { + setState(133); + _errHandler.sync(this); + switch (_input.LA(1)) { + case INT: + { + setState(128); + match(INT); + } + break; + case MON: + case TUE: + case WED: + case THU: + case FRI: + case SAT: + case SUN: + { + setState(129); + weekdayLiteral(); + } + break; + case JAN: + case FEB: + case MAR: + case APR: + case MAY: + case JUN: + case JUL: + case AUG: + case SEP: + case OCT: + case NOV: + case DEC: + { + setState(130); + monthLiteral(); + } + break; + case STAR: + { + setState(131); + match(STAR); + } + break; + case QUESTION: + { + setState(132); + match(QUESTION); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class PeriodicCronElementContext extends ParserRuleContext { + public TerminalCronElementContext terminalCronElement() { + return getRuleContext(TerminalCronElementContext.class,0); + } + public TerminalNode SLASH() { return getToken(CronParser.SLASH, 0); } + public RangeCronListContext rangeCronList() { + return getRuleContext(RangeCronListContext.class,0); + } + public PeriodicCronElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_periodicCronElement; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterPeriodicCronElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitPeriodicCronElement(this); + } + } + + public final PeriodicCronElementContext periodicCronElement() throws RecognitionException { + PeriodicCronElementContext _localctx = new PeriodicCronElementContext(_ctx, getState()); + enterRule(_localctx, 20, RULE_periodicCronElement); + try { + enterOuterAlt(_localctx, 1); + { + setState(135); + terminalCronElement(); + setState(136); + match(SLASH); + setState(137); + rangeCronList(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class RangeCronListContext extends ParserRuleContext { + public List rangeCronElement() { + return getRuleContexts(RangeCronElementContext.class); + } + public RangeCronElementContext rangeCronElement(int i) { + return getRuleContext(RangeCronElementContext.class,i); + } + public List COMMA() { return getTokens(CronParser.COMMA); } + public TerminalNode COMMA(int i) { + return getToken(CronParser.COMMA, i); + } + public RangeCronListContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_rangeCronList; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterRangeCronList(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitRangeCronList(this); + } + } + + public final RangeCronListContext rangeCronList() throws RecognitionException { + RangeCronListContext _localctx = new RangeCronListContext(_ctx, getState()); + enterRule(_localctx, 22, RULE_rangeCronList); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(139); + rangeCronElement(); + setState(144); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==COMMA) { + { + { + setState(140); + match(COMMA); + setState(141); + rangeCronElement(); + } + } + setState(146); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class NthDayOfWeekElementContext extends ParserRuleContext { + public List INT() { return getTokens(CronParser.INT); } + public TerminalNode INT(int i) { + return getToken(CronParser.INT, i); + } + public TerminalNode TAG() { return getToken(CronParser.TAG, 0); } + public WeekdayLiteralContext weekdayLiteral() { + return getRuleContext(WeekdayLiteralContext.class,0); + } + public NthDayOfWeekElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_nthDayOfWeekElement; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterNthDayOfWeekElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitNthDayOfWeekElement(this); + } + } + + public final NthDayOfWeekElementContext nthDayOfWeekElement() throws RecognitionException { + NthDayOfWeekElementContext _localctx = new NthDayOfWeekElementContext(_ctx, getState()); + enterRule(_localctx, 24, RULE_nthDayOfWeekElement); + try { + setState(154); + _errHandler.sync(this); + switch (_input.LA(1)) { + case INT: + enterOuterAlt(_localctx, 1); + { + setState(147); + match(INT); + setState(148); + match(TAG); + setState(149); + match(INT); + } + break; + case MON: + case TUE: + case WED: + case THU: + case FRI: + case SAT: + case SUN: + enterOuterAlt(_localctx, 2); + { + setState(150); + weekdayLiteral(); + setState(151); + match(TAG); + setState(152); + match(INT); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class LastDayOfWeekElementContext extends ParserRuleContext { + public TerminalNode INT() { return getToken(CronParser.INT, 0); } + public TerminalNode L() { return getToken(CronParser.L, 0); } + public WeekdayLiteralContext weekdayLiteral() { + return getRuleContext(WeekdayLiteralContext.class,0); + } + public LastDayOfWeekElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_lastDayOfWeekElement; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterLastDayOfWeekElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitLastDayOfWeekElement(this); + } + } + + public final LastDayOfWeekElementContext lastDayOfWeekElement() throws RecognitionException { + LastDayOfWeekElementContext _localctx = new LastDayOfWeekElementContext(_ctx, getState()); + enterRule(_localctx, 26, RULE_lastDayOfWeekElement); + try { + setState(161); + _errHandler.sync(this); + switch (_input.LA(1)) { + case INT: + enterOuterAlt(_localctx, 1); + { + setState(156); + match(INT); + setState(157); + match(L); + } + break; + case MON: + case TUE: + case WED: + case THU: + case FRI: + case SAT: + case SUN: + enterOuterAlt(_localctx, 2); + { + setState(158); + weekdayLiteral(); + setState(159); + match(L); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class NearestWeekDayToDayOfMonthElementContext extends ParserRuleContext { + public TerminalNode INT() { return getToken(CronParser.INT, 0); } + public TerminalNode W() { return getToken(CronParser.W, 0); } + public NearestWeekDayToDayOfMonthElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_nearestWeekDayToDayOfMonthElement; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterNearestWeekDayToDayOfMonthElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitNearestWeekDayToDayOfMonthElement(this); + } + } + + public final NearestWeekDayToDayOfMonthElementContext nearestWeekDayToDayOfMonthElement() throws RecognitionException { + NearestWeekDayToDayOfMonthElementContext _localctx = new NearestWeekDayToDayOfMonthElementContext(_ctx, getState()); + enterRule(_localctx, 28, RULE_nearestWeekDayToDayOfMonthElement); + try { + enterOuterAlt(_localctx, 1); + { + setState(163); + match(INT); + setState(164); + match(W); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class LastDayOfMonthElementContext extends ParserRuleContext { + public TerminalNode L() { return getToken(CronParser.L, 0); } + public TerminalNode DASH() { return getToken(CronParser.DASH, 0); } + public TerminalNode INT() { return getToken(CronParser.INT, 0); } + public TerminalNode LW() { return getToken(CronParser.LW, 0); } + public LastDayOfMonthElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_lastDayOfMonthElement; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterLastDayOfMonthElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitLastDayOfMonthElement(this); + } + } + + public final LastDayOfMonthElementContext lastDayOfMonthElement() throws RecognitionException { + LastDayOfMonthElementContext _localctx = new LastDayOfMonthElementContext(_ctx, getState()); + enterRule(_localctx, 30, RULE_lastDayOfMonthElement); + int _la; + try { + setState(172); + _errHandler.sync(this); + switch (_input.LA(1)) { + case L: + enterOuterAlt(_localctx, 1); + { + setState(166); + match(L); + setState(169); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==DASH) { + { + setState(167); + match(DASH); + setState(168); + match(INT); + } + } + + } + break; + case LW: + enterOuterAlt(_localctx, 2); + { + setState(171); + match(LW); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class WeekdayLiteralContext extends ParserRuleContext { + public TerminalNode MON() { return getToken(CronParser.MON, 0); } + public TerminalNode TUE() { return getToken(CronParser.TUE, 0); } + public TerminalNode WED() { return getToken(CronParser.WED, 0); } + public TerminalNode THU() { return getToken(CronParser.THU, 0); } + public TerminalNode FRI() { return getToken(CronParser.FRI, 0); } + public TerminalNode SAT() { return getToken(CronParser.SAT, 0); } + public TerminalNode SUN() { return getToken(CronParser.SUN, 0); } + public WeekdayLiteralContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_weekdayLiteral; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterWeekdayLiteral(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitWeekdayLiteral(this); + } + } + + public final WeekdayLiteralContext weekdayLiteral() throws RecognitionException { + WeekdayLiteralContext _localctx = new WeekdayLiteralContext(_ctx, getState()); + enterRule(_localctx, 32, RULE_weekdayLiteral); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(174); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 520192L) != 0)) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class MonthLiteralContext extends ParserRuleContext { + public TerminalNode JAN() { return getToken(CronParser.JAN, 0); } + public TerminalNode FEB() { return getToken(CronParser.FEB, 0); } + public TerminalNode MAR() { return getToken(CronParser.MAR, 0); } + public TerminalNode APR() { return getToken(CronParser.APR, 0); } + public TerminalNode MAY() { return getToken(CronParser.MAY, 0); } + public TerminalNode JUN() { return getToken(CronParser.JUN, 0); } + public TerminalNode JUL() { return getToken(CronParser.JUL, 0); } + public TerminalNode AUG() { return getToken(CronParser.AUG, 0); } + public TerminalNode SEP() { return getToken(CronParser.SEP, 0); } + public TerminalNode OCT() { return getToken(CronParser.OCT, 0); } + public TerminalNode NOV() { return getToken(CronParser.NOV, 0); } + public TerminalNode DEC() { return getToken(CronParser.DEC, 0); } + public MonthLiteralContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_monthLiteral; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).enterMonthLiteral(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof CronParserListener ) ((CronParserListener)listener).exitMonthLiteral(this); + } + } + + public final MonthLiteralContext monthLiteral() throws RecognitionException { + MonthLiteralContext _localctx = new MonthLiteralContext(_ctx, getState()); + enterRule(_localctx, 34, RULE_monthLiteral); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(176); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 2146959360L) != 0)) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static final String _serializedATN = + "\u0004\u0001 \u00b3\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002"+ + "\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004\u0002"+ + "\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007\u0007\u0007\u0002"+ + "\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b\u0007\u000b\u0002"+ + "\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002\u000f\u0007\u000f"+ + "\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0001\u0000\u0005\u0000"+ + "&\b\u0000\n\u0000\f\u0000)\t\u0000\u0001\u0000\u0001\u0000\u0004\u0000"+ + "-\b\u0000\u000b\u0000\f\u0000.\u0001\u0000\u0001\u0000\u0004\u00003\b"+ + "\u0000\u000b\u0000\f\u00004\u0001\u0000\u0001\u0000\u0004\u00009\b\u0000"+ + "\u000b\u0000\f\u0000:\u0001\u0000\u0001\u0000\u0004\u0000?\b\u0000\u000b"+ + "\u0000\f\u0000@\u0001\u0000\u0001\u0000\u0004\u0000E\b\u0000\u000b\u0000"+ + "\f\u0000F\u0001\u0000\u0001\u0000\u0005\u0000K\b\u0000\n\u0000\f\u0000"+ + "N\t\u0000\u0001\u0000\u0005\u0000Q\b\u0000\n\u0000\f\u0000T\t\u0000\u0001"+ + "\u0000\u0001\u0000\u0005\u0000X\b\u0000\n\u0000\f\u0000[\t\u0000\u0003"+ + "\u0000]\b\u0000\u0001\u0001\u0001\u0001\u0001\u0002\u0001\u0002\u0001"+ + "\u0003\u0001\u0003\u0001\u0004\u0001\u0004\u0001\u0004\u0003\u0004h\b"+ + "\u0004\u0001\u0005\u0001\u0005\u0001\u0006\u0001\u0006\u0001\u0006\u0003"+ + "\u0006o\b\u0006\u0001\u0007\u0001\u0007\u0003\u0007s\b\u0007\u0001\b\u0001"+ + "\b\u0001\b\u0001\b\u0001\b\u0003\bz\b\b\u0005\b|\b\b\n\b\f\b\u007f\t\b"+ + "\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0003\t\u0086\b\t\u0001\n\u0001"+ + "\n\u0001\n\u0001\n\u0001\u000b\u0001\u000b\u0001\u000b\u0005\u000b\u008f"+ + "\b\u000b\n\u000b\f\u000b\u0092\t\u000b\u0001\f\u0001\f\u0001\f\u0001\f"+ + "\u0001\f\u0001\f\u0001\f\u0003\f\u009b\b\f\u0001\r\u0001\r\u0001\r\u0001"+ + "\r\u0001\r\u0003\r\u00a2\b\r\u0001\u000e\u0001\u000e\u0001\u000e\u0001"+ + "\u000f\u0001\u000f\u0001\u000f\u0003\u000f\u00aa\b\u000f\u0001\u000f\u0003"+ + "\u000f\u00ad\b\u000f\u0001\u0010\u0001\u0010\u0001\u0011\u0001\u0011\u0001"+ + "\u0011\u0000\u0000\u0012\u0000\u0002\u0004\u0006\b\n\f\u000e\u0010\u0012"+ + "\u0014\u0016\u0018\u001a\u001c\u001e \"\u0000\u0002\u0001\u0000\f\u0012"+ + "\u0001\u0000\u0013\u001e\u00bb\u0000\\\u0001\u0000\u0000\u0000\u0002^"+ + "\u0001\u0000\u0000\u0000\u0004`\u0001\u0000\u0000\u0000\u0006b\u0001\u0000"+ + "\u0000\u0000\bg\u0001\u0000\u0000\u0000\ni\u0001\u0000\u0000\u0000\fn"+ + "\u0001\u0000\u0000\u0000\u000er\u0001\u0000\u0000\u0000\u0010t\u0001\u0000"+ + "\u0000\u0000\u0012\u0085\u0001\u0000\u0000\u0000\u0014\u0087\u0001\u0000"+ + "\u0000\u0000\u0016\u008b\u0001\u0000\u0000\u0000\u0018\u009a\u0001\u0000"+ + "\u0000\u0000\u001a\u00a1\u0001\u0000\u0000\u0000\u001c\u00a3\u0001\u0000"+ + "\u0000\u0000\u001e\u00ac\u0001\u0000\u0000\u0000 \u00ae\u0001\u0000\u0000"+ + "\u0000\"\u00b0\u0001\u0000\u0000\u0000$&\u0005\u0001\u0000\u0000%$\u0001"+ + "\u0000\u0000\u0000&)\u0001\u0000\u0000\u0000\'%\u0001\u0000\u0000\u0000"+ + "\'(\u0001\u0000\u0000\u0000(*\u0001\u0000\u0000\u0000)\'\u0001\u0000\u0000"+ + "\u0000*,\u0003\u0002\u0001\u0000+-\u0005\u0001\u0000\u0000,+\u0001\u0000"+ + "\u0000\u0000-.\u0001\u0000\u0000\u0000.,\u0001\u0000\u0000\u0000./\u0001"+ + "\u0000\u0000\u0000/0\u0001\u0000\u0000\u000002\u0003\u0004\u0002\u0000"+ + "13\u0005\u0001\u0000\u000021\u0001\u0000\u0000\u000034\u0001\u0000\u0000"+ + "\u000042\u0001\u0000\u0000\u000045\u0001\u0000\u0000\u000056\u0001\u0000"+ + "\u0000\u000068\u0003\u0006\u0003\u000079\u0005\u0001\u0000\u000087\u0001"+ + "\u0000\u0000\u00009:\u0001\u0000\u0000\u0000:8\u0001\u0000\u0000\u0000"+ + ":;\u0001\u0000\u0000\u0000;<\u0001\u0000\u0000\u0000<>\u0003\b\u0004\u0000"+ + "=?\u0005\u0001\u0000\u0000>=\u0001\u0000\u0000\u0000?@\u0001\u0000\u0000"+ + "\u0000@>\u0001\u0000\u0000\u0000@A\u0001\u0000\u0000\u0000AB\u0001\u0000"+ + "\u0000\u0000BD\u0003\n\u0005\u0000CE\u0005\u0001\u0000\u0000DC\u0001\u0000"+ + "\u0000\u0000EF\u0001\u0000\u0000\u0000FD\u0001\u0000\u0000\u0000FG\u0001"+ + "\u0000\u0000\u0000GH\u0001\u0000\u0000\u0000HL\u0003\f\u0006\u0000IK\u0005"+ + "\u0001\u0000\u0000JI\u0001\u0000\u0000\u0000KN\u0001\u0000\u0000\u0000"+ + "LJ\u0001\u0000\u0000\u0000LM\u0001\u0000\u0000\u0000M]\u0001\u0000\u0000"+ + "\u0000NL\u0001\u0000\u0000\u0000OQ\u0005\u0001\u0000\u0000PO\u0001\u0000"+ + "\u0000\u0000QT\u0001\u0000\u0000\u0000RP\u0001\u0000\u0000\u0000RS\u0001"+ + "\u0000\u0000\u0000SU\u0001\u0000\u0000\u0000TR\u0001\u0000\u0000\u0000"+ + "UY\u0005 \u0000\u0000VX\u0005\u0001\u0000\u0000WV\u0001\u0000\u0000\u0000"+ + "X[\u0001\u0000\u0000\u0000YW\u0001\u0000\u0000\u0000YZ\u0001\u0000\u0000"+ + "\u0000Z]\u0001\u0000\u0000\u0000[Y\u0001\u0000\u0000\u0000\\\'\u0001\u0000"+ + "\u0000\u0000\\R\u0001\u0000\u0000\u0000]\u0001\u0001\u0000\u0000\u0000"+ + "^_\u0003\u000e\u0007\u0000_\u0003\u0001\u0000\u0000\u0000`a\u0003\u000e"+ + "\u0007\u0000a\u0005\u0001\u0000\u0000\u0000bc\u0003\u000e\u0007\u0000"+ + "c\u0007\u0001\u0000\u0000\u0000dh\u0003\u001c\u000e\u0000eh\u0003\u001e"+ + "\u000f\u0000fh\u0003\u000e\u0007\u0000gd\u0001\u0000\u0000\u0000ge\u0001"+ + "\u0000\u0000\u0000gf\u0001\u0000\u0000\u0000h\t\u0001\u0000\u0000\u0000"+ + "ij\u0003\u000e\u0007\u0000j\u000b\u0001\u0000\u0000\u0000ko\u0003\u0018"+ + "\f\u0000lo\u0003\u001a\r\u0000mo\u0003\u000e\u0007\u0000nk\u0001\u0000"+ + "\u0000\u0000nl\u0001\u0000\u0000\u0000nm\u0001\u0000\u0000\u0000o\r\u0001"+ + "\u0000\u0000\u0000ps\u0003\u0016\u000b\u0000qs\u0003\u0014\n\u0000rp\u0001"+ + "\u0000\u0000\u0000rq\u0001\u0000\u0000\u0000s\u000f\u0001\u0000\u0000"+ + "\u0000t}\u0003\u0012\t\u0000uy\u0005\u0004\u0000\u0000vz\u0005\u000b\u0000"+ + "\u0000wz\u0003 \u0010\u0000xz\u0003\"\u0011\u0000yv\u0001\u0000\u0000"+ + "\u0000yw\u0001\u0000\u0000\u0000yx\u0001\u0000\u0000\u0000z|\u0001\u0000"+ + "\u0000\u0000{u\u0001\u0000\u0000\u0000|\u007f\u0001\u0000\u0000\u0000"+ + "}{\u0001\u0000\u0000\u0000}~\u0001\u0000\u0000\u0000~\u0011\u0001\u0000"+ + "\u0000\u0000\u007f}\u0001\u0000\u0000\u0000\u0080\u0086\u0005\u000b\u0000"+ + "\u0000\u0081\u0086\u0003 \u0010\u0000\u0082\u0086\u0003\"\u0011\u0000"+ + "\u0083\u0086\u0005\u0002\u0000\u0000\u0084\u0086\u0005\u0003\u0000\u0000"+ + "\u0085\u0080\u0001\u0000\u0000\u0000\u0085\u0081\u0001\u0000\u0000\u0000"+ + "\u0085\u0082\u0001\u0000\u0000\u0000\u0085\u0083\u0001\u0000\u0000\u0000"+ + "\u0085\u0084\u0001\u0000\u0000\u0000\u0086\u0013\u0001\u0000\u0000\u0000"+ + "\u0087\u0088\u0003\u0012\t\u0000\u0088\u0089\u0005\u0005\u0000\u0000\u0089"+ + "\u008a\u0003\u0016\u000b\u0000\u008a\u0015\u0001\u0000\u0000\u0000\u008b"+ + "\u0090\u0003\u0010\b\u0000\u008c\u008d\u0005\u0006\u0000\u0000\u008d\u008f"+ + "\u0003\u0010\b\u0000\u008e\u008c\u0001\u0000\u0000\u0000\u008f\u0092\u0001"+ + "\u0000\u0000\u0000\u0090\u008e\u0001\u0000\u0000\u0000\u0090\u0091\u0001"+ + "\u0000\u0000\u0000\u0091\u0017\u0001\u0000\u0000\u0000\u0092\u0090\u0001"+ + "\u0000\u0000\u0000\u0093\u0094\u0005\u000b\u0000\u0000\u0094\u0095\u0005"+ + "\n\u0000\u0000\u0095\u009b\u0005\u000b\u0000\u0000\u0096\u0097\u0003 "+ + "\u0010\u0000\u0097\u0098\u0005\n\u0000\u0000\u0098\u0099\u0005\u000b\u0000"+ + "\u0000\u0099\u009b\u0001\u0000\u0000\u0000\u009a\u0093\u0001\u0000\u0000"+ + "\u0000\u009a\u0096\u0001\u0000\u0000\u0000\u009b\u0019\u0001\u0000\u0000"+ + "\u0000\u009c\u009d\u0005\u000b\u0000\u0000\u009d\u00a2\u0005\u0007\u0000"+ + "\u0000\u009e\u009f\u0003 \u0010\u0000\u009f\u00a0\u0005\u0007\u0000\u0000"+ + "\u00a0\u00a2\u0001\u0000\u0000\u0000\u00a1\u009c\u0001\u0000\u0000\u0000"+ + "\u00a1\u009e\u0001\u0000\u0000\u0000\u00a2\u001b\u0001\u0000\u0000\u0000"+ + "\u00a3\u00a4\u0005\u000b\u0000\u0000\u00a4\u00a5\u0005\b\u0000\u0000\u00a5"+ + "\u001d\u0001\u0000\u0000\u0000\u00a6\u00a9\u0005\u0007\u0000\u0000\u00a7"+ + "\u00a8\u0005\u0004\u0000\u0000\u00a8\u00aa\u0005\u000b\u0000\u0000\u00a9"+ + "\u00a7\u0001\u0000\u0000\u0000\u00a9\u00aa\u0001\u0000\u0000\u0000\u00aa"+ + "\u00ad\u0001\u0000\u0000\u0000\u00ab\u00ad\u0005\t\u0000\u0000\u00ac\u00a6"+ + "\u0001\u0000\u0000\u0000\u00ac\u00ab\u0001\u0000\u0000\u0000\u00ad\u001f"+ + "\u0001\u0000\u0000\u0000\u00ae\u00af\u0007\u0000\u0000\u0000\u00af!\u0001"+ + "\u0000\u0000\u0000\u00b0\u00b1\u0007\u0001\u0000\u0000\u00b1#\u0001\u0000"+ + "\u0000\u0000\u0015\'.4:@FLRY\\gnry}\u0085\u0090\u009a\u00a1\u00a9\u00ac"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; + for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { + _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); + } + } +} \ No newline at end of file diff --git a/headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronParserBaseListener.java b/headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronParserBaseListener.java new file mode 100644 index 000000000..fcc9cb162 --- /dev/null +++ b/headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronParserBaseListener.java @@ -0,0 +1,256 @@ +// Generated from CronParser.g4 by ANTLR 4.13.1 +package org.springframework.ide.vscode.parser.cron; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * This class provides an empty implementation of {@link CronParserListener}, + * which can be extended to create a listener which only needs to handle a subset + * of the available methods. + */ +@SuppressWarnings("CheckReturnValue") +public class CronParserBaseListener implements CronParserListener { + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterCronExpression(CronParser.CronExpressionContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitCronExpression(CronParser.CronExpressionContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterSecondsElement(CronParser.SecondsElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitSecondsElement(CronParser.SecondsElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterMinutesElement(CronParser.MinutesElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitMinutesElement(CronParser.MinutesElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterHoursElement(CronParser.HoursElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitHoursElement(CronParser.HoursElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterDaysElement(CronParser.DaysElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitDaysElement(CronParser.DaysElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterMonthsElement(CronParser.MonthsElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitMonthsElement(CronParser.MonthsElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterDaysOfWeekElement(CronParser.DaysOfWeekElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitDaysOfWeekElement(CronParser.DaysOfWeekElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterCronElement(CronParser.CronElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitCronElement(CronParser.CronElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterRangeCronElement(CronParser.RangeCronElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitRangeCronElement(CronParser.RangeCronElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterTerminalCronElement(CronParser.TerminalCronElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitTerminalCronElement(CronParser.TerminalCronElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterPeriodicCronElement(CronParser.PeriodicCronElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitPeriodicCronElement(CronParser.PeriodicCronElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterRangeCronList(CronParser.RangeCronListContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitRangeCronList(CronParser.RangeCronListContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterNthDayOfWeekElement(CronParser.NthDayOfWeekElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitNthDayOfWeekElement(CronParser.NthDayOfWeekElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterLastDayOfWeekElement(CronParser.LastDayOfWeekElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitLastDayOfWeekElement(CronParser.LastDayOfWeekElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterNearestWeekDayToDayOfMonthElement(CronParser.NearestWeekDayToDayOfMonthElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitNearestWeekDayToDayOfMonthElement(CronParser.NearestWeekDayToDayOfMonthElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterLastDayOfMonthElement(CronParser.LastDayOfMonthElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitLastDayOfMonthElement(CronParser.LastDayOfMonthElementContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterWeekdayLiteral(CronParser.WeekdayLiteralContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitWeekdayLiteral(CronParser.WeekdayLiteralContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterMonthLiteral(CronParser.MonthLiteralContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitMonthLiteral(CronParser.MonthLiteralContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterEveryRule(ParserRuleContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitEveryRule(ParserRuleContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void visitTerminal(TerminalNode node) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void visitErrorNode(ErrorNode node) { } +} \ No newline at end of file diff --git a/headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronParserListener.java b/headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronParserListener.java new file mode 100644 index 000000000..b7da6fe2f --- /dev/null +++ b/headless-services/commons/jpql/src/main/java/org/springframework/ide/vscode/parser/cron/CronParserListener.java @@ -0,0 +1,190 @@ +// Generated from CronParser.g4 by ANTLR 4.13.1 +package org.springframework.ide.vscode.parser.cron; +import org.antlr.v4.runtime.tree.ParseTreeListener; + +/** + * This interface defines a complete listener for a parse tree produced by + * {@link CronParser}. + */ +public interface CronParserListener extends ParseTreeListener { + /** + * Enter a parse tree produced by {@link CronParser#cronExpression}. + * @param ctx the parse tree + */ + void enterCronExpression(CronParser.CronExpressionContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#cronExpression}. + * @param ctx the parse tree + */ + void exitCronExpression(CronParser.CronExpressionContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#secondsElement}. + * @param ctx the parse tree + */ + void enterSecondsElement(CronParser.SecondsElementContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#secondsElement}. + * @param ctx the parse tree + */ + void exitSecondsElement(CronParser.SecondsElementContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#minutesElement}. + * @param ctx the parse tree + */ + void enterMinutesElement(CronParser.MinutesElementContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#minutesElement}. + * @param ctx the parse tree + */ + void exitMinutesElement(CronParser.MinutesElementContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#hoursElement}. + * @param ctx the parse tree + */ + void enterHoursElement(CronParser.HoursElementContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#hoursElement}. + * @param ctx the parse tree + */ + void exitHoursElement(CronParser.HoursElementContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#daysElement}. + * @param ctx the parse tree + */ + void enterDaysElement(CronParser.DaysElementContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#daysElement}. + * @param ctx the parse tree + */ + void exitDaysElement(CronParser.DaysElementContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#monthsElement}. + * @param ctx the parse tree + */ + void enterMonthsElement(CronParser.MonthsElementContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#monthsElement}. + * @param ctx the parse tree + */ + void exitMonthsElement(CronParser.MonthsElementContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#daysOfWeekElement}. + * @param ctx the parse tree + */ + void enterDaysOfWeekElement(CronParser.DaysOfWeekElementContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#daysOfWeekElement}. + * @param ctx the parse tree + */ + void exitDaysOfWeekElement(CronParser.DaysOfWeekElementContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#cronElement}. + * @param ctx the parse tree + */ + void enterCronElement(CronParser.CronElementContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#cronElement}. + * @param ctx the parse tree + */ + void exitCronElement(CronParser.CronElementContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#rangeCronElement}. + * @param ctx the parse tree + */ + void enterRangeCronElement(CronParser.RangeCronElementContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#rangeCronElement}. + * @param ctx the parse tree + */ + void exitRangeCronElement(CronParser.RangeCronElementContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#terminalCronElement}. + * @param ctx the parse tree + */ + void enterTerminalCronElement(CronParser.TerminalCronElementContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#terminalCronElement}. + * @param ctx the parse tree + */ + void exitTerminalCronElement(CronParser.TerminalCronElementContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#periodicCronElement}. + * @param ctx the parse tree + */ + void enterPeriodicCronElement(CronParser.PeriodicCronElementContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#periodicCronElement}. + * @param ctx the parse tree + */ + void exitPeriodicCronElement(CronParser.PeriodicCronElementContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#rangeCronList}. + * @param ctx the parse tree + */ + void enterRangeCronList(CronParser.RangeCronListContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#rangeCronList}. + * @param ctx the parse tree + */ + void exitRangeCronList(CronParser.RangeCronListContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#nthDayOfWeekElement}. + * @param ctx the parse tree + */ + void enterNthDayOfWeekElement(CronParser.NthDayOfWeekElementContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#nthDayOfWeekElement}. + * @param ctx the parse tree + */ + void exitNthDayOfWeekElement(CronParser.NthDayOfWeekElementContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#lastDayOfWeekElement}. + * @param ctx the parse tree + */ + void enterLastDayOfWeekElement(CronParser.LastDayOfWeekElementContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#lastDayOfWeekElement}. + * @param ctx the parse tree + */ + void exitLastDayOfWeekElement(CronParser.LastDayOfWeekElementContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#nearestWeekDayToDayOfMonthElement}. + * @param ctx the parse tree + */ + void enterNearestWeekDayToDayOfMonthElement(CronParser.NearestWeekDayToDayOfMonthElementContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#nearestWeekDayToDayOfMonthElement}. + * @param ctx the parse tree + */ + void exitNearestWeekDayToDayOfMonthElement(CronParser.NearestWeekDayToDayOfMonthElementContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#lastDayOfMonthElement}. + * @param ctx the parse tree + */ + void enterLastDayOfMonthElement(CronParser.LastDayOfMonthElementContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#lastDayOfMonthElement}. + * @param ctx the parse tree + */ + void exitLastDayOfMonthElement(CronParser.LastDayOfMonthElementContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#weekdayLiteral}. + * @param ctx the parse tree + */ + void enterWeekdayLiteral(CronParser.WeekdayLiteralContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#weekdayLiteral}. + * @param ctx the parse tree + */ + void exitWeekdayLiteral(CronParser.WeekdayLiteralContext ctx); + /** + * Enter a parse tree produced by {@link CronParser#monthLiteral}. + * @param ctx the parse tree + */ + void enterMonthLiteral(CronParser.MonthLiteralContext ctx); + /** + * Exit a parse tree produced by {@link CronParser#monthLiteral}. + * @param ctx the parse tree + */ + void exitMonthLiteral(CronParser.MonthLiteralContext ctx); +} \ No newline at end of file diff --git a/headless-services/spring-boot-language-server/pom.xml b/headless-services/spring-boot-language-server/pom.xml index 5b0f394a2..e0fcd5f43 100644 --- a/headless-services/spring-boot-language-server/pom.xml +++ b/headless-services/spring-boot-language-server/pom.xml @@ -61,6 +61,11 @@ org.springframework spring-web + + org.springframework.ide.vscode + commons-util + ${dependencies.version} + org.springframework.ide.vscode commons-yaml @@ -196,6 +201,11 @@ ${dependencies.version} test + + org.springframework.ide.vscode + commons-util + 1.57.0-SNAPSHOT + diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/CronConfig.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/CronConfig.java new file mode 100644 index 000000000..9368a797d --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/CronConfig.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2024 Broadcom, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.app; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.ide.vscode.boot.java.cron.CronReconciler; +import org.springframework.ide.vscode.boot.java.cron.CronSemanticTokens; + +@Configuration(proxyBeanMethods = false) +public class CronConfig { + + @Bean + CronSemanticTokens cronSemanticTokens() { + return new CronSemanticTokens(); + } + + @Bean + CronReconciler cronReconciler() { + return new CronReconciler(); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/JdtConfig.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/JdtConfig.java index 5fbe5c709..f7e3cf703 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/JdtConfig.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/JdtConfig.java @@ -15,6 +15,10 @@ import java.util.Optional; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.ide.vscode.boot.java.cron.CronReconciler; +import org.springframework.ide.vscode.boot.java.cron.CronSemanticTokens; +import org.springframework.ide.vscode.boot.java.cron.JdtCronReconciler; +import org.springframework.ide.vscode.boot.java.cron.JdtCronSemanticTokensProvider; import org.springframework.ide.vscode.boot.java.data.jpa.queries.HqlSemanticTokens; import org.springframework.ide.vscode.boot.java.data.jpa.queries.JdtDataQuerySemanticTokensProvider; import org.springframework.ide.vscode.boot.java.data.jpa.queries.JpqlSemanticTokens; @@ -126,6 +130,10 @@ public class JdtConfig { return new JdtDataQuerySemanticTokensProvider(jpqlProvider, hqlProvider, supportState, spelSemanticTokens); } + @Bean JdtCronSemanticTokensProvider jdtCronSemanticTokensProvider(CronSemanticTokens cronProvider) { + return new JdtCronSemanticTokensProvider(cronProvider); + } + @Bean QueryJdtAstReconciler dataQueryReconciler( @Qualifier("hqlReconciler") Reconciler hqlReconciler, @Qualifier("jpqlReconciler") Reconciler jpqlReconciler, @@ -148,5 +156,9 @@ public class JdtConfig { @Bean ImplicitWebAnnotationNamesReconciler implicitWebAnnotationNamesReconciler(SimpleLanguageServer server) { return new ImplicitWebAnnotationNamesReconciler(server.getQuickfixRegistry()); } + + @Bean JdtCronReconciler jdtCronReconciler(CronReconciler cronReconciler) { + return new JdtCronReconciler(cronReconciler); + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/SpringProblemCategories.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/SpringProblemCategories.java index 7579a99bf..b5eebd1a5 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/SpringProblemCategories.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/SpringProblemCategories.java @@ -38,7 +38,10 @@ public class SpringProblemCategories { public static final ProblemCategory VERSION_VALIDATION = new ProblemCategory("version-validation", "Versions and Support Ranges", new Toggle("Enablement", EnumSet.of(OFF, ON), ON, "boot-java.validation.java.version-validation")); - public static final ProblemCategory DATA_QUERY = new ProblemCategory("data-query", "Data Query", + public static final ProblemCategory DATA_QUERY = new ProblemCategory("data-query", "Data Queries", new Toggle("Enablement", EnumSet.of(OFF, ON), ON, "boot-java.validation.data-query")); + public static final ProblemCategory CRON = new ProblemCategory("cron", "CRON Expressions", + new Toggle("Enablement", EnumSet.of(OFF, ON), ON, "boot-java.validation.cron")); + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Annotations.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Annotations.java index 5a0721af7..ba065ce07 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Annotations.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Annotations.java @@ -83,6 +83,8 @@ public class Annotations { public static final String NAMED_JAVAX = "javax.inject.Named"; public static final String NAMED_JAKARTA = "jakarta.inject.Named"; + public static final String SCHEDULED = "org.springframework.scheduling.annotation.Scheduled"; + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/CronProblemType.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/CronProblemType.java new file mode 100644 index 000000000..455089489 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/CronProblemType.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2024 Broadcom, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.cron; + +import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.ERROR; + +import org.springframework.ide.vscode.boot.common.SpringProblemCategories; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; + +public enum CronProblemType implements ProblemType { + + SYNTAX(ERROR, "Syntax", "Cron Expression Syntax"), + FIELD(ERROR, "Field", "Cron Expression field"); + + private final ProblemSeverity defaultSeverity; + private String description; + private String label; + + private CronProblemType(ProblemSeverity defaultSeverity, String description) { + this(defaultSeverity, description, null); + } + + private CronProblemType(ProblemSeverity defaultSeverity, String description, String label) { + this.description = description; + this.defaultSeverity = defaultSeverity; + this.label = label; + } + + @Override + public ProblemSeverity getDefaultSeverity() { + return defaultSeverity; + } + + public String getLabel() { + if (label==null) { + label = createDefaultLabel(); + } + return label; + } + + @Override + public String getDescription() { + return description; + } + + private String createDefaultLabel() { + String label = this.toString().substring(5).toLowerCase().replace('_', ' '); + return Character.toUpperCase(label.charAt(0)) + label.substring(1); + } + + @Override + public String getCode() { + return name(); + } + + @Override + public ProblemCategory getCategory() { + return SpringProblemCategories.CRON; + } +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/CronReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/CronReconciler.java new file mode 100644 index 000000000..f551357e6 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/CronReconciler.java @@ -0,0 +1,237 @@ +/******************************************************************************* + * Copyright (c) 2024 Broadcom, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.cron; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.BitSet; +import java.util.concurrent.atomic.AtomicReference; + +import org.antlr.v4.runtime.ANTLRErrorListener; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ConsoleErrorListener; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.atn.ATNConfigSet; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.RuleNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.java.handlers.Reconciler; +import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl; +import org.springframework.ide.vscode.commons.util.BadLocationException; +import org.springframework.ide.vscode.commons.util.text.linetracker.DefaultLineTracker; +import org.springframework.ide.vscode.parser.cron.CronLexer; +import org.springframework.ide.vscode.parser.cron.CronParser; +import org.springframework.ide.vscode.parser.cron.CronParser.DaysElementContext; +import org.springframework.ide.vscode.parser.cron.CronParser.DaysOfWeekElementContext; +import org.springframework.ide.vscode.parser.cron.CronParser.HoursElementContext; +import org.springframework.ide.vscode.parser.cron.CronParser.MinutesElementContext; +import org.springframework.ide.vscode.parser.cron.CronParser.MonthsElementContext; +import org.springframework.ide.vscode.parser.cron.CronParser.SecondsElementContext; +import org.springframework.ide.vscode.parser.cron.CronParserBaseListener; + +public class CronReconciler implements Reconciler { + + private static final Logger log = LoggerFactory.getLogger(CronReconciler.class); + + private Method parseSeconds; + private Method parseMinutes; + private Method parseHours; + private Method parseDaysOfMonth; + private Method parseMonth; + private Method parseDaysOfWeek; + + public CronReconciler() { + initReflection(); + } + + @Override + public void reconcile(String text, int startPosition, IProblemCollector problemCollector) { + CronLexer lexer = new CronLexer(CharStreams.fromString(text)); + CommonTokenStream antlrTokens = new CommonTokenStream(lexer); + CronParser parser = new CronParser(antlrTokens); + AtomicReference lineTrackerRef = new AtomicReference<>(); + + lexer.removeErrorListener(ConsoleErrorListener.INSTANCE); + parser.removeErrorListener(ConsoleErrorListener.INSTANCE); + + parser.addParseListener(new CronParserBaseListener() { + + @Override + public void exitSecondsElement(SecondsElementContext ctx) { + validate(ctx, parseSeconds); + } + + @Override + public void exitMinutesElement(MinutesElementContext ctx) { + validate(ctx, parseMinutes); + } + + @Override + public void exitHoursElement(HoursElementContext ctx) { + validate(ctx, parseHours); + } + + @Override + public void exitDaysElement(DaysElementContext ctx) { + validate(ctx, parseDaysOfMonth); + } + + @Override + public void exitMonthsElement(MonthsElementContext ctx) { + validate(ctx, parseMonth); + } + + @Override + public void exitDaysOfWeekElement(DaysOfWeekElementContext ctx) { + validate(ctx, parseDaysOfWeek); + } + + private void validate(ParserRuleContext ctx, Method m) { + if (!hasSyntaxErrors(ctx) && m != null) { + try { + m.invoke(null, ctx.getText()); + } catch (IllegalAccessException e) { + log.error("", e); + } catch (IllegalArgumentException e) { + log.error("", e); + } catch (InvocationTargetException e) { + Throwable target = e.getTargetException(); + String message = target.getMessage(); + if (message.startsWith("For input string:")) { + markProblemsForNumberFormatException(ctx, message); + } else { + problemCollector.accept(new ReconcileProblemImpl(CronProblemType.FIELD, "CRON: %s".formatted(message), startPosition + ctx.getStart().getStartIndex(), ctx.getText().length())); + } + } + } + } + + private void markProblemsForNumberFormatException(ParserRuleContext ctx, String message) { + int start = message.indexOf("\""); + if (start >= 0) { + int end = message.lastIndexOf("\""); + if (start + 1 < end) { + String text = ctx.getText(); + String problemText = message.substring(start + 1, end); + int offset = text.indexOf(problemText); + if (offset >= 0) { + problemCollector.accept(new ReconcileProblemImpl(CronProblemType.FIELD, "CRON: Number expected", startPosition + ctx.getStart().getStartIndex() + offset, problemText.length())); + return; + } + } + } + problemCollector.accept(new ReconcileProblemImpl(CronProblemType.FIELD, "CRON: %s".formatted(message), startPosition + ctx.getStart().getStartIndex(), ctx.getText().length())); + } + + + }); + + ANTLRErrorListener antlrErrorListener = new ANTLRErrorListener() { + + @Override + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, + String msg, RecognitionException e) { + int offset = 0; + int length = 1; + if (offendingSymbol instanceof Token) { + Token token = (Token) offendingSymbol; + offset = token.getStartIndex(); + length = token.getText().length(); + } else { + DefaultLineTracker lt = lineTrackerRef.get(); + if (lt == null) { + lt = new DefaultLineTracker(); + lt.set(text); + lineTrackerRef.set(lt); + } + try { + offset = lt.getLineOffset(line - 1) + charPositionInLine; + } catch (BadLocationException e1) { + log.error("", e1); + } + } + problemCollector.accept(new ReconcileProblemImpl(CronProblemType.SYNTAX, "CRON: " + msg, startPosition + offset, length)); + } + + @Override + public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, int prediction, + ATNConfigSet configs) { + } + + @Override + public void reportAttemptingFullContext(Parser recognizer, DFA dfa, int startIndex, int stopIndex, + BitSet conflictingAlts, ATNConfigSet configs) { + } + + @Override + public void reportAmbiguity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, boolean exact, + BitSet ambigAlts, ATNConfigSet configs) { + } + }; + + lexer.addErrorListener(antlrErrorListener); + parser.addErrorListener(antlrErrorListener); + + parser.cronExpression(); + } + + private boolean hasSyntaxErrors(ParserRuleContext ctx) { + return ctx.accept(new AbstractParseTreeVisitor() { + + @Override + public Boolean visitErrorNode(ErrorNode node) { + return true; + } + + @Override + protected Boolean defaultResult() { + return false; + } + + @Override + protected boolean shouldVisitNextChild(RuleNode node, Boolean currentResult) { + return currentResult == null || !currentResult.booleanValue(); + } + + }); + + } + + private void initReflection() { + try { + Class clazz = Class.forName("org.springframework.scheduling.support.CronField"); + parseSeconds = clazz.getDeclaredMethod("parseSeconds", String.class); + parseSeconds.setAccessible(true); + parseMinutes = clazz.getDeclaredMethod("parseMinutes", String.class); + parseMinutes.setAccessible(true); + parseHours = clazz.getDeclaredMethod("parseHours", String.class); + parseHours.setAccessible(true); + parseDaysOfMonth = clazz.getDeclaredMethod("parseDaysOfMonth", String.class); + parseDaysOfMonth.setAccessible(true); + parseMonth = clazz.getDeclaredMethod("parseMonth", String.class); + parseMonth.setAccessible(true); + parseDaysOfWeek = clazz.getDeclaredMethod("parseDaysOfWeek", String.class); + parseDaysOfWeek.setAccessible(true); + } catch (Exception e) { + log.error("", e); + } + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/CronSemanticTokens.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/CronSemanticTokens.java new file mode 100644 index 000000000..8a3706ad6 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/CronSemanticTokens.java @@ -0,0 +1,170 @@ +/******************************************************************************* + * Copyright (c) 2024 Broadcom, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.cron; + +import static org.springframework.ide.vscode.parser.cron.CronLexer.*; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.antlr.v4.runtime.ANTLRErrorListener; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ConsoleErrorListener; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.atn.ATNConfigSet; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.TerminalNode; +import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData; +import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokensDataProvider; +import org.springframework.ide.vscode.parser.cron.CronLexer; +import org.springframework.ide.vscode.parser.cron.CronParser; +import org.springframework.ide.vscode.parser.spel.SpelParserBaseListener; + +public class CronSemanticTokens implements SemanticTokensDataProvider { + + private final Optional> parseErrorHandler; + + public CronSemanticTokens(Optional> parseErrorHandler) { + this.parseErrorHandler = parseErrorHandler; + } + + public CronSemanticTokens() { + this(Optional.empty()); + } + + @Override + public List getTokenTypes() { + return List.of("operator", "number", "enum", "macro", "method"); + } + + @Override + public List computeTokens(String text, int initialOffset) { + CronLexer lexer = new CronLexer(CharStreams.fromString(text)); + CommonTokenStream antlrTokens = new CommonTokenStream(lexer); + CronParser parser = new CronParser(antlrTokens); + + Map semantics = new HashMap<>(); + + lexer.removeErrorListener(ConsoleErrorListener.INSTANCE); + parser.removeErrorListener(ConsoleErrorListener.INSTANCE); + + List tokens = new ArrayList<>(); + + parser.addParseListener(new SpelParserBaseListener() { + + private void processTerminalNode(TerminalNode node) { + Token token = node.getSymbol(); + switch (token.getType()) { + case COMMA: + case DASH: + case QUESTION: + case SLASH: + case STAR: + case TAG: + semantics.put(token, "operator"); + break; + case L: + case W: + case LW: + semantics.put(token, "method"); + break; + case MON: + case TUE: + case WED: + case THU: + case FRI: + case SAT: + case SUN: + case JAN: + case FEB: + case MAR: + case APR: + case MAY: + case JUN: + case JUL: + case AUG: + case SEP: + case OCT: + case NOV: + case DEC: + case IDENTIFIER: + semantics.put(token, "enum"); + break; + case INT: + semantics.put(token, "number"); + break; + case MACRO: + semantics.put(token, "macro"); + } + } + + @Override + public void visitTerminal(TerminalNode node) { + processTerminalNode(node); + } + + @Override + public void visitErrorNode(ErrorNode node) { + processTerminalNode(node); + } + }); + + parser.addErrorListener(new ANTLRErrorListener() { + + @Override + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, + String msg, RecognitionException e) { + parseErrorHandler.ifPresent(h -> h.accept(e)); + } + + @Override + public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, int prediction, + ATNConfigSet configs) { + } + + @Override + public void reportAttemptingFullContext(Parser recognizer, DFA dfa, int startIndex, int stopIndex, + BitSet conflictingAlts, ATNConfigSet configs) { + } + + @Override + public void reportAmbiguity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, boolean exact, + BitSet ambigAlts, ATNConfigSet configs) { + } + }); + + + parser.cronExpression(); + + tokens.addAll(semantics.entrySet().stream() + .map(e -> new SemanticTokenData(e.getKey().getStartIndex() + initialOffset, + e.getKey().getStartIndex() + e.getKey().getText().length() + initialOffset, e.getValue(), + new String[0])) + .collect(Collectors.toList())); + + Collections.sort(tokens); + + return tokens; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/JdtCronReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/JdtCronReconciler.java new file mode 100644 index 000000000..06ff38e57 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/JdtCronReconciler.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2024 Broadcom, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.cron; + +import java.net.URI; + +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.MemberValuePair; +import org.eclipse.jdt.core.dom.NormalAnnotation; +import org.springframework.ide.vscode.boot.java.Annotations; +import org.springframework.ide.vscode.boot.java.data.jpa.queries.QueryJdtAstReconciler; +import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.SpringProjectUtil; +import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; + +public class JdtCronReconciler implements JdtAstReconciler { + + private final CronReconciler cronReconciler; + + public JdtCronReconciler(CronReconciler cronReconciler) { + this.cronReconciler = cronReconciler; + } + + @Override + public boolean isApplicable(IJavaProject project) { + return SpringProjectUtil.hasDependencyStartingWith(project, "spring-context", null); + } + + @Override + public ProblemType getProblemType() { + return CronProblemType.SYNTAX; + } + + @Override + public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu, + IProblemCollector problemCollector, boolean isCompleteAst) { + return new ASTVisitor() { + @Override + public boolean visit(NormalAnnotation node) { + if (node.getTypeName() != null) { + String fqn = node.getTypeName().getFullyQualifiedName(); + if (JdtCronSemanticTokensProvider.SCHEDULED_SIMPLE_NAME.equals(fqn) || Annotations.SCHEDULED.equals(fqn)) { + ITypeBinding typeBinding = node.resolveTypeBinding(); + if (typeBinding != null && Annotations.SCHEDULED.equals(typeBinding.getQualifiedName())) { + for (Object value : node.values()) { + if (value instanceof MemberValuePair) { + MemberValuePair pair = (MemberValuePair) value; + String name = pair.getName().getFullyQualifiedName(); + if (name != null && "cron".equals(name)) { + QueryJdtAstReconciler.reconcileExpression(cronReconciler, pair.getValue(), problemCollector); + } + } + } + } + } + } + return super.visit(node); + } + }; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/JdtCronSemanticTokensProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/JdtCronSemanticTokensProvider.java new file mode 100644 index 000000000..49d325057 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/cron/JdtCronSemanticTokensProvider.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2024 Broadcom, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.cron; + +import java.util.List; + +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.MemberValuePair; +import org.eclipse.jdt.core.dom.NormalAnnotation; +import org.springframework.ide.vscode.boot.java.Annotations; +import org.springframework.ide.vscode.boot.java.JdtSemanticTokensProvider; +import org.springframework.ide.vscode.boot.java.data.jpa.queries.JdtDataQuerySemanticTokensProvider; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.SpringProjectUtil; +import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData; +import org.springframework.ide.vscode.commons.util.Collector; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +public class JdtCronSemanticTokensProvider implements JdtSemanticTokensProvider { + + final private CronSemanticTokens tokensProvider; + + static final String SCHEDULED_SIMPLE_NAME = "Scheduled"; + + + public JdtCronSemanticTokensProvider(CronSemanticTokens tokensProvider) { + this.tokensProvider = tokensProvider; + } + + @Override + public List getTokenTypes() { + return tokensProvider.getTokenTypes(); + } + + @Override + public List getTokenModifiers() { + return tokensProvider.getTypeModifiers(); + } + + @Override + public boolean isApplicable(IJavaProject project) { + return SpringProjectUtil.hasDependencyStartingWith(project, "spring-context", null); + } + + @Override + public ASTVisitor getTokensComputer(IJavaProject project, TextDocument doc, CompilationUnit cu, + Collector collector) { + return new ASTVisitor() { + + @Override + public boolean visit(NormalAnnotation node) { + if (node.getTypeName() != null) { + String fqn = node.getTypeName().getFullyQualifiedName(); + if (SCHEDULED_SIMPLE_NAME.equals(fqn) || Annotations.SCHEDULED.equals(fqn)) { + ITypeBinding typeBinding = node.resolveTypeBinding(); + if (typeBinding != null && Annotations.SCHEDULED.equals(typeBinding.getQualifiedName())) { + for (Object value : node.values()) { + if (value instanceof MemberValuePair) { + MemberValuePair pair = (MemberValuePair) value; + String name = pair.getName().getFullyQualifiedName(); + if (name != null && "cron".equals(name)) { + JdtDataQuerySemanticTokensProvider.computeTokensForExpression(tokensProvider, pair.getValue()).forEach(collector::accept); + } + } + } + } + } + } + return super.visit(node); + } + + }; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/AntlrReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/AntlrReconciler.java index 02cd6e2d1..e008c345e 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/AntlrReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/AntlrReconciler.java @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.boot.java.data.jpa.queries; import java.util.BitSet; +import java.util.concurrent.atomic.AtomicReference; import org.antlr.v4.runtime.ANTLRErrorListener; import org.antlr.v4.runtime.CharStream; @@ -31,6 +32,8 @@ import org.springframework.ide.vscode.boot.java.handlers.Reconciler; import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl; +import org.springframework.ide.vscode.commons.util.BadLocationException; +import org.springframework.ide.vscode.commons.util.text.linetracker.DefaultLineTracker; public class AntlrReconciler implements Reconciler { @@ -41,6 +44,9 @@ public class AntlrReconciler implements Reconciler { private final Class lexerClass; private final String parseMethod; private final ProblemType problemType; + + // To quickly get around issues with token recognition coming from the Lexer as it might to much to handle in terms of fixing the parser/lexer right away + protected boolean errorOnUnrecognizedTokens; public AntlrReconciler(String prefix, Class parserClass, Class lexerClass, String parseMethod, ProblemType problemType) { @@ -59,19 +65,33 @@ public class AntlrReconciler implements Reconciler { lexer.removeErrorListener(ConsoleErrorListener.INSTANCE); parser.removeErrorListener(ConsoleErrorListener.INSTANCE); - parser.addErrorListener(new ANTLRErrorListener() { + AtomicReference lineTrackerRef = new AtomicReference<>(); + + ANTLRErrorListener antlrErrorListener = new ANTLRErrorListener() { @Override public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { - Token token = (Token) offendingSymbol; - int offset = token.getStartIndex(); - int length = token.getStopIndex() - token.getStartIndex() + 1; - if (token.getStartIndex() >= token.getStopIndex()) { - offset = token.getStartIndex() - token.getCharPositionInLine(); - length = token.getCharPositionInLine() + 1; + int offset = 0; + int length = 1; + if (offendingSymbol instanceof Token) { + Token token = (Token) offendingSymbol; + offset = token.getStartIndex(); + length = token.getText().length(); + } else { + DefaultLineTracker lt = lineTrackerRef.get(); + if (lt == null) { + lt = new DefaultLineTracker(); + lt.set(text); + lineTrackerRef.set(lt); + } + try { + offset = lt.getLineOffset(line - 1) + charPositionInLine; + } catch (BadLocationException e1) { + log.error("", e1); + } } - problemCollector.accept(new ReconcileProblemImpl(problemType, prefix + ": " + msg, startPosition + offset, length)); + problemCollector.accept(new ReconcileProblemImpl(problemType, String.format("%s: %s", prefix, msg), startPosition + offset, length)); } @Override @@ -88,7 +108,12 @@ public class AntlrReconciler implements Reconciler { public void reportAmbiguity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, boolean exact, BitSet ambigAlts, ATNConfigSet configs) { } - }); + }; + + if (errorOnUnrecognizedTokens) { + lexer.addErrorListener(antlrErrorListener); + } + parser.addErrorListener(antlrErrorListener); return parser; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/JdtDataQuerySemanticTokensProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/JdtDataQuerySemanticTokensProvider.java index 92346840d..d1702a41c 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/JdtDataQuerySemanticTokensProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/JdtDataQuerySemanticTokensProvider.java @@ -121,9 +121,9 @@ public class JdtDataQuerySemanticTokensProvider implements JdtSemanticTokensProv if (queryExpression != null) { if (isNative) { - computeTokensForQueryExpression(getSqlSemanticTokensProvider(jp), queryExpression).forEach(tokensData::accept); + computeTokensForExpression(getSqlSemanticTokensProvider(jp), queryExpression).forEach(tokensData::accept); } else { - computeTokensForQueryExpression(provider, queryExpression).forEach(tokensData::accept); + computeTokensForExpression(provider, queryExpression).forEach(tokensData::accept); } } @@ -133,7 +133,7 @@ public class JdtDataQuerySemanticTokensProvider implements JdtSemanticTokensProv @Override public boolean visit(SingleMemberAnnotation a) { if (isQueryAnnotation(a)) { - computeTokensForQueryExpression(provider, a.getValue()).forEach(tokensData::accept); + computeTokensForExpression(provider, a.getValue()).forEach(tokensData::accept); } return false; } @@ -144,7 +144,7 @@ public class JdtDataQuerySemanticTokensProvider implements JdtSemanticTokensProv IMethodBinding methodBinding = node.resolveMethodBinding(); if ("jakarta.persistence.EntityManager".equals(methodBinding.getDeclaringClass().getQualifiedName())) { if (methodBinding.getParameterTypes().length <= 2 && "java.lang.String".equals(methodBinding.getParameterTypes()[0].getQualifiedName())) { - computeTokensForQueryExpression(provider, queryExpr).forEach(tokensData::accept); + computeTokensForExpression(provider, queryExpr).forEach(tokensData::accept); } } } @@ -153,7 +153,7 @@ public class JdtDataQuerySemanticTokensProvider implements JdtSemanticTokensProv }; } - private static List computeTokensForQueryExpression(SemanticTokensDataProvider provider, Expression valueExp) { + public static List computeTokensForExpression(SemanticTokensDataProvider provider, Expression valueExp) { String query = null; int offset = 0; if (valueExp instanceof StringLiteral sl) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/QueryJdtAstReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/QueryJdtAstReconciler.java index 948b439a8..b592bad74 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/QueryJdtAstReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/QueryJdtAstReconciler.java @@ -146,7 +146,7 @@ public class QueryJdtAstReconciler implements JdtAstReconciler { return SpringProjectUtil.hasDependencyStartingWith(project, "hibernate-core", null) ? hqlReconciler : jpqlReconciler; } - private void reconcileExpression(Reconciler reconciler, Expression valueExp, IProblemCollector problemCollector) { + public static void reconcileExpression(Reconciler reconciler, Expression valueExp, IProblemCollector problemCollector) { String query = null; int offset = 0; if (valueExp instanceof StringLiteral sl) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/spel/SpelReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/spel/SpelReconciler.java index b582b2adc..772922de2 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/spel/SpelReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/spel/SpelReconciler.java @@ -22,6 +22,7 @@ public class SpelReconciler extends AntlrReconciler { public SpelReconciler() { super("SPEL", SpelParser.class, SpelLexer.class, "spelExpr", SpelProblemType.JAVA_SPEL_EXPRESSION_SYNTAX); + this.errorOnUnrecognizedTokens = false; this.enabled = true; } diff --git a/headless-services/spring-boot-language-server/src/main/resources/problem-types.json b/headless-services/spring-boot-language-server/src/main/resources/problem-types.json index be2725f79..41f150abd 100644 --- a/headless-services/spring-boot-language-server/src/main/resources/problem-types.json +++ b/headless-services/spring-boot-language-server/src/main/resources/problem-types.json @@ -397,7 +397,7 @@ }, { "id": "data-query", - "label": "Data Query", + "label": "Data Queries", "toggle": { "label": "Enablement", "values": [ @@ -428,5 +428,33 @@ "defaultSeverity": "ERROR" } ] + }, + { + "id": "cron", + "label": "CRON Expressions", + "toggle": { + "label": "Enablement", + "values": [ + "OFF", + "ON" + ], + "preferenceKey": "boot-java.validation.cron", + "defaultValue": "ON" + }, + "order": 9, + "problemTypes": [ + { + "code": "SYNTAX", + "label": "Cron Expression Syntax", + "description": "Syntax", + "defaultSeverity": "ERROR" + }, + { + "code": "FIELD", + "label": "Cron Expression field", + "description": "Field", + "defaultSeverity": "ERROR" + } + ] } ] \ No newline at end of file diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/CronReconcilerTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/CronReconcilerTest.java new file mode 100644 index 000000000..842e4ba0c --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/CronReconcilerTest.java @@ -0,0 +1,75 @@ +package org.springframework.ide.vscode.boot.java.cron; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.ide.vscode.commons.languageserver.reconcile.BasicProblemCollector; +import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem; + +public class CronReconcilerTest { + + private CronReconciler reconciler = new CronReconciler(); + private List problems; + private IProblemCollector collector; + + @BeforeEach + void setup() { + problems = new ArrayList<>(); + collector = new BasicProblemCollector(problems); + } + + @Test + void noProblems_1() { + reconciler.reconcile("0 0 0 L-3 * *", 0, collector); + assertEquals(0, problems.size()); + } + + @Test + void DayOfTheWeekProblems_1() { + reconciler.reconcile("0 0 0 8 * MAR-JUL", 0, collector); + assertEquals(1, problems.size()); + assertReconcileProblem(problems.get(0), CronProblemType.FIELD, 10, 6); + } + + @Test + void noProblems_3() { + reconciler.reconcile("MAR-JUL 0 0 8 * *", 0, collector); + assertEquals(1, problems.size()); + assertReconcileProblem(problems.get(0), CronProblemType.FIELD, 0, 7); + } + + @Test + void syntax_and_field_problems_1() { + reconciler.reconcile("0 0 0 8LW * MARCH-JUL", 0, collector); + assertEquals(3, problems.size()); + assertReconcileProblem(problems.get(0), CronProblemType.SYNTAX, 7, 2); + assertReconcileProblem(problems.get(1), CronProblemType.SYNTAX, 12, 5); + assertReconcileProblem(problems.get(2), CronProblemType.FIELD, 18, 2); + } + + @Test + void syntax_problems_2() { + reconciler.reconcile("qq#3 0 Blah 1-88LW * JUL-MARCH", 0, collector); + assertEquals(1, problems.size()); + assertReconcileProblem(problems.get(0), CronProblemType.SYNTAX, 0, 2); + } + + @Test + void syntax_problems_3() { + reconciler.reconcile("10/2. * * ? * MON-5", 0, collector); + assertEquals(1, problems.size()); + assertReconcileProblem(problems.get(0), CronProblemType.SYNTAX, 4, 1); + } + + static void assertReconcileProblem(ReconcileProblem p, ProblemType type, int offset, int length) { + assertEquals(offset, p.getOffset()); + assertEquals(length, p.getLength()); + assertEquals(type, p.getType()); + } +} diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/CronSemanticTokensTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/CronSemanticTokensTest.java new file mode 100644 index 000000000..d034fe7a0 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/CronSemanticTokensTest.java @@ -0,0 +1,277 @@ +/******************************************************************************* + * Copyright (c) 2024 Broadcom, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.cron; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData; + +public class CronSemanticTokensTest { + + private CronSemanticTokens provider; + + @BeforeEach + void setup() { + provider = new CronSemanticTokens(Optional.of(Assertions::fail)); + } + + @Test + void topHoueEveryDayEveryWeek() { + List tokens = provider.computeTokens("0 0 * * * *", 0); + assertThat(tokens.size()).isEqualTo(6); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "operator", new String[0])); + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(6, 7, "operator", new String[0])); + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(8, 9, "operator", new String[0])); + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(10, 11, "operator", new String[0])); + } + + @Test + void everyTenSeconds() { + List tokens = provider.computeTokens("*/10 * * * * *", 0); + assertThat(tokens.size()).isEqualTo(8); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "operator", new String[0])); + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(1, 2, "operator", new String[0])); + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(2, 4, "number", new String[0])); + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(5, 6, "operator", new String[0])); + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(7, 8, "operator", new String[0])); + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(9, 10, "operator", new String[0])); + assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(11, 12, "operator", new String[0])); + assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(13, 14, "operator", new String[0])); + } + + @Test + void betweenEightAndTenEveryDay() { + List tokens = provider.computeTokens("0 0 8-10 * * *", 0); + assertThat(tokens.size()).isEqualTo(8); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); // 0 + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); // 0 + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "number", new String[0])); // 8 + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(5, 6, "operator", new String[0])); // - + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(6, 8, "number", new String[0])); // 10 + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(9, 10, "operator", new String[0])); // * + assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(11, 12, "operator", new String[0])); // * + assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(13, 14, "operator", new String[0])); // * + } + + @Test + void everyDayBetweenSixAndSeven() { + List tokens = provider.computeTokens("0 0 6,19 * * *", 0); + assertThat(tokens.size()).isEqualTo(8); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); // 0 + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); // 0 + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "number", new String[0])); // 6 + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(5, 6, "operator", new String[0])); // , + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(6, 8, "number", new String[0])); // 19 + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(9, 10, "operator", new String[0])); // * + assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(11, 12, "operator", new String[0])); // * + assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(13, 14, "operator", new String[0])); // * + } + + @Test + void everyHalfHourBetweenEightAndEleven() { + List tokens = provider.computeTokens("0 0/30 8-10 * * *", 0); + assertThat(tokens.size()).isEqualTo(10); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); // 0 + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); // 0 + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(3, 4, "operator", new String[0])); // / + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(4, 6, "number", new String[0])); // 30 + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(7, 8, "number", new String[0])); // 8 + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(8, 9, "operator", new String[0])); // - + assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(9, 11, "number", new String[0])); // 10 + assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(12, 13, "operator", new String[0])); // * + assertThat(tokens.get(8)).isEqualTo(new SemanticTokenData(14, 15, "operator", new String[0])); // * + assertThat(tokens.get(9)).isEqualTo(new SemanticTokenData(16, 17, "operator", new String[0])); // * + } + + @Test + void nineToFiveOnWeekdays() { + List tokens = provider.computeTokens("0 0 9-17 * * MON-FRI", 0); + assertThat(tokens.size()).isEqualTo(10); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); // 0 + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); // 0 + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "number", new String[0])); // 9 + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(5, 6, "operator", new String[0])); // - + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(6, 8, "number", new String[0])); // 17 + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(9, 10, "operator", new String[0])); // * + assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(11, 12, "operator", new String[0])); // * + assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(13, 16, "enum", new String[0])); // MON + assertThat(tokens.get(8)).isEqualTo(new SemanticTokenData(16, 17, "operator", new String[0])); // - + assertThat(tokens.get(9)).isEqualTo(new SemanticTokenData(17, 20, "enum", new String[0])); // FRI + } + + @Test + void christmas() { + List tokens = provider.computeTokens("0 0 0 25 12 ?", 0); + assertThat(tokens.size()).isEqualTo(6); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); // 0 + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); // 0 + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "number", new String[0])); // 0 + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(6, 8, "number", new String[0])); // 25 + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(9, 11, "number", new String[0])); // 12 + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(12, 13, "operator", new String[0])); // ? + } + + @Test + void lastDayOfMonthAtMidnight() { + List tokens = provider.computeTokens("0 0 0 L * *", 0); + assertThat(tokens.size()).isEqualTo(6); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); // 0 + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); // 0 + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "number", new String[0])); // 0 + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(6, 7, "method", new String[0])); // L + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(8, 9, "operator", new String[0])); // * + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(10, 11, "operator", new String[0])); // * + } + + @Test + void thirdToLasttDayOfMonthAtMidnight() { + List tokens = provider.computeTokens("0 0 0 L-3 * *", 0); + assertThat(tokens.size()).isEqualTo(8); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); // 0 + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); // 0 + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "number", new String[0])); // 0 + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(6, 7, "method", new String[0])); // L + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(7, 8, "operator", new String[0])); // - + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(8, 9, "number", new String[0])); // 3 + assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(10, 11, "operator", new String[0])); // * + assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(12, 13, "operator", new String[0])); // * + } + + @Test + void firstWeekDayOfMonthAtMidnight() { + List tokens = provider.computeTokens("0 0 0 1W * *", 0); + assertThat(tokens.size()).isEqualTo(7); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); // 0 + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); // 0 + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "number", new String[0])); // 0 + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(6, 7, "number", new String[0])); // 1 + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(7, 8, "method", new String[0])); // W + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(9, 10, "operator", new String[0])); // * + assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(11, 12, "operator", new String[0])); // * + } + + @Test + void lastWeekDayOfMonthAtMidnight() { + List tokens = provider.computeTokens("0 0 0 LW * *", 0); + assertThat(tokens.size()).isEqualTo(6); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); // 0 + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); // 0 + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "number", new String[0])); // 0 + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(6, 8, "method", new String[0])); // LW + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(9, 10, "operator", new String[0])); // * + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(11, 12, "operator", new String[0])); // * + } + + @Test + void lastFridayOfMonthAtMidnight() { + List tokens = provider.computeTokens("0 0 0 * * 5L", 0); + assertThat(tokens.size()).isEqualTo(7); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); // 0 + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); // 0 + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "number", new String[0])); // 0 + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(6, 7, "operator", new String[0])); // * + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(8, 9, "operator", new String[0])); // * + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(10, 11, "number", new String[0])); // 5 + assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(11, 12, "method", new String[0])); // L + } + + @Test + void lastThursdayOfMonthAtMidnight() { + List tokens = provider.computeTokens("0 0 0 * * THUL", 0); + assertThat(tokens.size()).isEqualTo(7); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); // 0 + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); // 0 + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "number", new String[0])); // 0 + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(6, 7, "operator", new String[0])); // * + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(8, 9, "operator", new String[0])); // * + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(10, 13, "enum", new String[0])); // THU + assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(13, 14, "method", new String[0])); // L + } + + @Test + void secondFridayOfMonthAtMidnight() { + List tokens = provider.computeTokens("0 0 0 ? * 5#2", 0); + assertThat(tokens.size()).isEqualTo(8); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); // 0 + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); // 0 + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "number", new String[0])); // 0 + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(6, 7, "operator", new String[0])); // ? + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(8, 9, "operator", new String[0])); // * + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(10, 11, "number", new String[0])); // 5 + assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(11, 12, "operator", new String[0])); // # + assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(12, 13, "number", new String[0])); // 2 + } + + @Test + void firstMondayOfMonthAtMidnight() { + List tokens = provider.computeTokens("0 0 0 ? * MON#1", 0); + assertThat(tokens.size()).isEqualTo(8); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); // 0 + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); // 0 + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "number", new String[0])); // 0 + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(6, 7, "operator", new String[0])); // ? + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(8, 9, "operator", new String[0])); // * + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(10, 13, "enum", new String[0])); // MON + assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(13, 14, "operator", new String[0])); // # + assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(14, 15, "number", new String[0])); // 1 + } + + @Test + void macro() { + List tokens = provider.computeTokens("@yearly", 0); + assertThat(tokens.size()).isEqualTo(1); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 7, "macro", new String[0])); // @yearly + } + + @Test + void errors_1() { + provider = new CronSemanticTokens(); + List tokens = provider.computeTokens("0 0 0 8LW * Foo#bar", 0); + assertThat(tokens.size()).isEqualTo(9); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0])); // 0 + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "number", new String[0])); // 0 + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "number", new String[0])); // 0 + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(6, 7, "number", new String[0])); // 8 + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(7, 9, "method", new String[0])); // LW + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(10, 11, "operator", new String[0])); // * + assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(12, 15, "enum", new String[0])); // Foo + assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(15, 16, "operator", new String[0])); // # + assertThat(tokens.get(8)).isEqualTo(new SemanticTokenData(16, 19, "enum", new String[0])); // bar + } + + @Test + void errors_2() { + provider = new CronSemanticTokens(); + List tokens = provider.computeTokens("qq#3 0 Blah 1-88LW * JUL-MARCH", 0); + assertThat(tokens.size()).isEqualTo(13); + assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 2, "enum", new String[0])); // qq + assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "operator", new String[0])); // # + assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(3, 4, "number", new String[0])); // 3 + assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(5, 6, "number", new String[0])); // 0 + assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(7, 11, "enum", new String[0])); // Blah + assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(12, 13, "number", new String[0])); // 1 + assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(13, 14, "operator", new String[0])); // - + assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(14, 16, "number", new String[0])); // 88 + assertThat(tokens.get(8)).isEqualTo(new SemanticTokenData(16, 18, "method", new String[0])); // LW + assertThat(tokens.get(9)).isEqualTo(new SemanticTokenData(19, 20, "operator", new String[0])); // * + assertThat(tokens.get(10)).isEqualTo(new SemanticTokenData(21, 24, "enum", new String[0])); // JUL + assertThat(tokens.get(11)).isEqualTo(new SemanticTokenData(24, 25, "operator", new String[0])); // - + assertThat(tokens.get(12)).isEqualTo(new SemanticTokenData(25, 30, "enum", new String[0])); // MARCH + } +} diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/JdtCronReconcilerTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/JdtCronReconcilerTest.java new file mode 100644 index 000000000..ddd66066b --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/JdtCronReconcilerTest.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2024 Broadcom, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.cron; + +import java.io.File; + +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; +import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest; +import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.util.Settings; +import org.springframework.ide.vscode.commons.util.text.LanguageId; +import org.springframework.ide.vscode.languageserver.testharness.Editor; +import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness; +import org.springframework.ide.vscode.project.harness.ProjectsHarness; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; + +@ExtendWith(SpringExtension.class) +@BootLanguageServerTest +@Import(SymbolProviderTestConf.class) +public class JdtCronReconcilerTest { + + @Autowired + private BootLanguageServerHarness harness; + @Autowired + private JavaProjectFinder projectFinder; + + private File directory; + + @BeforeEach + public void setup() throws Exception { + harness.intialize(null); + + directory = new File(ProjectsHarness.class.getResource("/test-projects/boot-mysql/").toURI()); + + String projectDir = directory.toURI().toString(); + + // trigger project creation + projectFinder.find(new TextDocumentIdentifier(projectDir)).get(); + + String changedSettings = """ + { + "boot-java": { + "validation": { + "cron": "ON" + } + } + } + """; + JsonElement settingsAsJson = new Gson().fromJson(changedSettings, JsonElement.class); + harness.changeConfiguration(new Settings(settingsAsJson)); + } + + @Test + void noErrors() throws Exception { + String source = """ + package example.demo; + + import org.springframework.scheduling.annotation.Scheduled; + + public class A { + + @Scheduled(cron = "0 0 0 L-3 * *") + void foo() {} + + } + """; + String docUri = directory.toPath().resolve("src/main/java/example/demo/A.java").toUri() + .toString(); + Editor editor = harness.newEditor(LanguageId.JAVA, source, docUri); + editor.assertProblems(); + } + + @Test + void errorsReported_1() throws Exception { + String source = """ + package example.demo; + + import org.springframework.scheduling.annotation.Scheduled; + + public class A { + + @Scheduled(cron = "0 0 0 8LW * MARCH-JUL") + void foo() {} + + } + """; + String docUri = directory.toPath().resolve("src/main/java/example/demo/A.java").toUri() + .toString(); + Editor editor = harness.newEditor(LanguageId.JAVA, source, docUri); + editor.assertProblems( + "LW|CRON: extraneous input 'LW'", + "MARCH|CRON: extraneous input 'MARCH' expecting", + "JU|CRON: Number expected" + ); + } + + @Test + void errorsReported_2() throws Exception { + String source = """ + package example.demo; + + import org.springframework.scheduling.annotation.Scheduled; + + public class A { + + @Scheduled(cron = "10/? * * ? * MON-5") + void foo() {} + + } + """; + String docUri = directory.toPath().resolve("src/main/java/example/demo/A.java").toUri() + .toString(); + Editor editor = harness.newEditor(LanguageId.JAVA, source, docUri); + editor.assertProblems( + "?|CRON: Number expected" + ); + } +} diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/JdtCronSemanticTokensProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/JdtCronSemanticTokensProviderTest.java new file mode 100644 index 000000000..0b33a5a00 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/cron/JdtCronSemanticTokensProviderTest.java @@ -0,0 +1,370 @@ +/******************************************************************************* + * Copyright (c) 2024 Broadcom, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.cron; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.file.Paths; +import java.util.List; + +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; +import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest; +import org.springframework.ide.vscode.boot.bootiful.HoverTestConf; +import org.springframework.ide.vscode.boot.java.reconcilers.CompositeASTVisitor; +import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; +import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData; +import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; +import org.springframework.ide.vscode.commons.util.Collector; +import org.springframework.ide.vscode.project.harness.ProjectsHarness; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@BootLanguageServerTest +@Import(HoverTestConf.class) +public class JdtCronSemanticTokensProviderTest { + + @Autowired + JdtCronSemanticTokensProvider provider; + + private ProjectsHarness projects = ProjectsHarness.INSTANCE; + + private MavenJavaProject jp; + + @BeforeEach + public void setup() throws Exception { + jp = projects.mavenProject("spring-modulith-example-full"); + } + + private List computeTokens(CompilationUnit cu) { + Collector collector = new Collector<>(); + CompositeASTVisitor visitor = new CompositeASTVisitor(); + visitor.add(provider.getTokensComputer(jp, null, cu, collector)); + cu.accept(visitor); + return collector.get(); + } + + @Test + void secondFridayOfMonthAtMidnight() throws Exception { + String source = """ + package my.package + + import org.springframework.scheduling.annotation.Scheduled; + + public class A { + + @Scheduled(cron="0 0 0 ? * 5#2") + void foo() {} + + } + """; + + String uri = Paths.get(jp.getLocationUri()).resolve("src/main/resource/my/package/A.java").toUri() + .toASCIIString(); + CompilationUnit cu = CompilationUnitCache.parse2(source.toCharArray(), uri, "A.java", jp); + + assertThat(cu).isNotNull(); + + List tokens = computeTokens(cu); + + assertThat(tokens.size()).isEqualTo(8); + + SemanticTokenData token = tokens.get(0); + assertThat(token).isEqualTo(new SemanticTokenData(117, 118, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("0"); + + token = tokens.get(1); + assertThat(token).isEqualTo(new SemanticTokenData(119, 120, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("0"); + + token = tokens.get(2); + assertThat(token).isEqualTo(new SemanticTokenData(121, 122, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("0"); + + token = tokens.get(3); + assertThat(token).isEqualTo(new SemanticTokenData(123, 124, "operator", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("?"); + + token = tokens.get(4); + assertThat(token).isEqualTo(new SemanticTokenData(125, 126, "operator", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("*"); + + token = tokens.get(5); + assertThat(token).isEqualTo(new SemanticTokenData(127, 128, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("5"); + + token = tokens.get(6); + assertThat(token).isEqualTo(new SemanticTokenData(128, 129, "operator", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("#"); + + token = tokens.get(7); + assertThat(token).isEqualTo(new SemanticTokenData(129, 130, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("2"); + + } + + @Test + void lastThursdayOfMonthAtMidnight() throws Exception { + String source = """ + package my.package + + import org.springframework.scheduling.annotation.Scheduled; + + public class A { + + @Scheduled(cron="0 0 0 * * THUL") + void foo() {} + + } + """; + + String uri = Paths.get(jp.getLocationUri()).resolve("src/main/resource/my/package/A.java").toUri() + .toASCIIString(); + CompilationUnit cu = CompilationUnitCache.parse2(source.toCharArray(), uri, "A.java", jp); + + assertThat(cu).isNotNull(); + + List tokens = computeTokens(cu); + + assertThat(tokens.size()).isEqualTo(7); + + SemanticTokenData token = tokens.get(0); + assertThat(token).isEqualTo(new SemanticTokenData(117, 118, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("0"); + + token = tokens.get(1); + assertThat(token).isEqualTo(new SemanticTokenData(119, 120, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("0"); + + token = tokens.get(2); + assertThat(token).isEqualTo(new SemanticTokenData(121, 122, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("0"); + + token = tokens.get(3); + assertThat(token).isEqualTo(new SemanticTokenData(123, 124, "operator", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("*"); + + token = tokens.get(4); + assertThat(token).isEqualTo(new SemanticTokenData(125, 126, "operator", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("*"); + + token = tokens.get(5); + assertThat(token).isEqualTo(new SemanticTokenData(127, 130, "enum", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("THU"); + + token = tokens.get(6); + assertThat(token).isEqualTo(new SemanticTokenData(130, 131, "method", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("L"); + + } + + @Test + void thirdToLasttDayOfMonthAtMidnight() throws Exception { + String source = """ + package my.package + + import org.springframework.scheduling.annotation.Scheduled; + + public class A { + + @Scheduled(cron="0 0 0 L-3 * *") + void foo() {} + + } + """; + + String uri = Paths.get(jp.getLocationUri()).resolve("src/main/resource/my/package/A.java").toUri() + .toASCIIString(); + CompilationUnit cu = CompilationUnitCache.parse2(source.toCharArray(), uri, "A.java", jp); + + assertThat(cu).isNotNull(); + + List tokens = computeTokens(cu); + + assertThat(tokens.size()).isEqualTo(8); + + SemanticTokenData token = tokens.get(0); + assertThat(token).isEqualTo(new SemanticTokenData(117, 118, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("0"); + + token = tokens.get(1); + assertThat(token).isEqualTo(new SemanticTokenData(119, 120, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("0"); + + token = tokens.get(2); + assertThat(token).isEqualTo(new SemanticTokenData(121, 122, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("0"); + + token = tokens.get(3); + assertThat(token).isEqualTo(new SemanticTokenData(123, 124, "method", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("L"); + + token = tokens.get(4); + assertThat(token).isEqualTo(new SemanticTokenData(124, 125, "operator", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("-"); + + token = tokens.get(5); + assertThat(token).isEqualTo(new SemanticTokenData(125, 126, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("3"); + + token = tokens.get(6); + assertThat(token).isEqualTo(new SemanticTokenData(127, 128, "operator", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("*"); + + token = tokens.get(7); + assertThat(token).isEqualTo(new SemanticTokenData(129, 130, "operator", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("*"); + } + + @Test + void errors_1() throws Exception { + String source = """ + package my.package + + import org.springframework.scheduling.annotation.Scheduled; + + public class A { + + @Scheduled(cron="0 0 0 8LW * Foo#bar") + void foo() {} + + } + """; + + String uri = Paths.get(jp.getLocationUri()).resolve("src/main/resource/my/package/A.java").toUri() + .toASCIIString(); + CompilationUnit cu = CompilationUnitCache.parse2(source.toCharArray(), uri, "A.java", jp); + + assertThat(cu).isNotNull(); + + List tokens = computeTokens(cu); + + assertThat(tokens.size()).isEqualTo(9); + + SemanticTokenData token = tokens.get(0); + assertThat(token).isEqualTo(new SemanticTokenData(117, 118, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("0"); + + token = tokens.get(1); + assertThat(token).isEqualTo(new SemanticTokenData(119, 120, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("0"); + + token = tokens.get(2); + assertThat(token).isEqualTo(new SemanticTokenData(121, 122, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("0"); + + token = tokens.get(3); + assertThat(token).isEqualTo(new SemanticTokenData(123, 124, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("8"); + + token = tokens.get(4); + assertThat(token).isEqualTo(new SemanticTokenData(124, 126, "method", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("LW"); + + token = tokens.get(5); + assertThat(token).isEqualTo(new SemanticTokenData(127, 128, "operator", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("*"); + + token = tokens.get(6); + assertThat(token).isEqualTo(new SemanticTokenData(129, 132, "enum", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("Foo"); + + token = tokens.get(7); + assertThat(token).isEqualTo(new SemanticTokenData(132, 133, "operator", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("#"); + + token = tokens.get(8); + assertThat(token).isEqualTo(new SemanticTokenData(133, 136, "enum", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("bar"); + } + + @Test + void errors_2() throws Exception { + String source = """ + package my.package + + import org.springframework.scheduling.annotation.Scheduled; + + public class A { + + @Scheduled(cron="qq#3 0 Blah 1-88LW * JUL-MARCH") + void foo() {} + + } + """; + + String uri = Paths.get(jp.getLocationUri()).resolve("src/main/resource/my/package/A.java").toUri() + .toASCIIString(); + CompilationUnit cu = CompilationUnitCache.parse2(source.toCharArray(), uri, "A.java", jp); + + assertThat(cu).isNotNull(); + + List tokens = computeTokens(cu); + + assertThat(tokens.size()).isEqualTo(13); + + SemanticTokenData token = tokens.get(0); + assertThat(token).isEqualTo(new SemanticTokenData(117, 119, "enum", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("qq"); + + token = tokens.get(1); + assertThat(token).isEqualTo(new SemanticTokenData(119, 120, "operator", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("#"); + + token = tokens.get(2); + assertThat(token).isEqualTo(new SemanticTokenData(120, 121, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("3"); + + token = tokens.get(3); + assertThat(token).isEqualTo(new SemanticTokenData(122, 123, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("0"); + + token = tokens.get(4); + assertThat(token).isEqualTo(new SemanticTokenData(124, 128, "enum", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("Blah"); + + token = tokens.get(5); + assertThat(token).isEqualTo(new SemanticTokenData(129, 130, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("1"); + + token = tokens.get(6); + assertThat(token).isEqualTo(new SemanticTokenData(130, 131, "operator", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("-"); + + token = tokens.get(7); + assertThat(token).isEqualTo(new SemanticTokenData(131, 133, "number", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("88"); + + token = tokens.get(8); + assertThat(token).isEqualTo(new SemanticTokenData(133, 135, "method", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("LW"); + + token = tokens.get(9); + assertThat(token).isEqualTo(new SemanticTokenData(136, 137, "operator", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("*"); + + token = tokens.get(10); + assertThat(token).isEqualTo(new SemanticTokenData(138, 141, "enum", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("JUL"); + + token = tokens.get(11); + assertThat(token).isEqualTo(new SemanticTokenData(141, 142, "operator", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("-"); + + token = tokens.get(12); + assertThat(token).isEqualTo(new SemanticTokenData(142, 147, "enum", new String[0])); + assertThat(source.substring(token.start(), token.end())).isEqualTo("MARCH"); + } +} diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesToJson.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesToJson.java index c693cd52d..b8648277a 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesToJson.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesToJson.java @@ -10,6 +10,8 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.test; +import static org.junit.jupiter.api.Assertions.assertEquals; + import java.io.File; import java.io.FileReader; import java.io.IOException; @@ -24,12 +26,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.io.FileUtils; - -import static org.junit.jupiter.api.Assertions.assertEquals; import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType; import org.springframework.ide.vscode.boot.java.Boot3JavaProblemType; import org.springframework.ide.vscode.boot.java.SpelProblemType; import org.springframework.ide.vscode.boot.java.SpringAotJavaProblemType; +import org.springframework.ide.vscode.boot.java.cron.CronProblemType; import org.springframework.ide.vscode.boot.java.data.jpa.queries.QueryProblemType; import org.springframework.ide.vscode.boot.properties.reconcile.ApplicationPropertiesProblemType; import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationProblemType; @@ -179,6 +180,7 @@ public class ProblemTypesToJson { writer.collectProblemTypeData(SpringAotJavaProblemType.values()); writer.collectProblemTypeData(VersionValidationProblemType.values()); writer.collectProblemTypeData(QueryProblemType.values()); + writer.collectProblemTypeData(CronProblemType.values()); Collections.sort(writer.problemCategories); diff --git a/vscode-extensions/commons-vscode/package-lock.json b/vscode-extensions/commons-vscode/package-lock.json index 3dc6492af..37e7697c6 100644 --- a/vscode-extensions/commons-vscode/package-lock.json +++ b/vscode-extensions/commons-vscode/package-lock.json @@ -30,10 +30,13 @@ "integrity": "sha512-NjADvQGJRECRkNts2xUALwPj/KJkvCOkwkm+/v9pP6mKhVtMh2wABaeAggae4x7+hBNJH0V0mw60Y0kk/rbSpw==" }, "node_modules/@types/node": { - "version": "18.11.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", - "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", - "dev": true + "version": "18.19.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.45.tgz", + "integrity": "sha512-VZxPKNNhjKmaC1SUYowuXSRSMGyQGmQjvvA1xE4QZ0xce2kLtEhPDS+kqpCPBZYgqblCLQ2DAjSzmgCM5auvhA==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/vscode": { "version": "1.75.0", @@ -41,6 +44,21 @@ "integrity": "sha512-SAr0PoOhJS6FUq5LjNr8C/StBKALZwDVm3+U4pjF/3iYkt3GioJOPV/oB1Sf1l7lROe4TgrMyL5N1yaEgTWycw==", "dev": true }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/async": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", @@ -50,9 +68,12 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -76,12 +97,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -96,15 +123,16 @@ } }, "node_modules/deep-equal": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz", - "integrity": "sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", "dependencies": { - "call-bind": "^1.0.2", - "es-get-iterator": "^1.1.2", - "get-intrinsic": "^1.1.3", + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.1", + "is-array-buffer": "^3.0.2", "is-date-object": "^1.0.5", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", @@ -112,21 +140,41 @@ "object-is": "^1.1.5", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", + "regexp.prototype.flags": "^1.5.1", "side-channel": "^1.0.4", "which-boxed-primitive": "^1.0.2", "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dependencies": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, @@ -137,6 +185,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-get-iterator": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", @@ -165,9 +232,12 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/functions-have-names": { "version": "1.2.3", @@ -178,13 +248,18 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -201,17 +276,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -221,11 +285,22 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -243,11 +318,11 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -256,13 +331,24 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/internal-slot": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", - "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", "side-channel": "^1.0.4" }, "engines": { @@ -285,13 +371,15 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", - "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-typed-array": "^1.1.10" + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -349,9 +437,12 @@ } }, "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -386,19 +477,25 @@ } }, "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -432,17 +529,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "engines": { "node": ">= 0.4" }, @@ -450,21 +540,16 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -480,18 +565,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", @@ -505,9 +578,9 @@ } }, "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -529,20 +602,23 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -560,12 +636,12 @@ } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, @@ -589,14 +665,23 @@ "node": ">= 0.12.0" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -606,13 +691,10 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -620,14 +702,48 @@ "node": ">=10" } }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -657,6 +773,12 @@ "node": ">=4.2.0" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/vscode-jsonrpc": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", @@ -712,30 +834,14 @@ } }, "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -744,11 +850,23 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } } } } diff --git a/vscode-extensions/vscode-spring-boot/package.json b/vscode-extensions/vscode-spring-boot/package.json index 19e96cfa0..88e0f7677 100644 --- a/vscode-extensions/vscode-spring-boot/package.json +++ b/vscode-extensions/vscode-spring-boot/package.json @@ -1173,7 +1173,7 @@ }, { "id": "data-query", - "title": "Data Query", + "title": "Data Queries", "order": 408, "properties": { "boot-java.validation.data-query": { @@ -1222,6 +1222,46 @@ ] } } + }, + { + "id": "cron", + "title": "CRON Expressions", + "order": 409, + "properties": { + "boot-java.validation.cron": { + "type": "string", + "default": "ON", + "description": "Enablement", + "enum": [ + "OFF", + "ON" + ] + }, + "spring-boot.ls.problem.cron.SYNTAX": { + "type": "string", + "default": "ERROR", + "description": "Syntax", + "enum": [ + "IGNORE", + "INFO", + "WARNING", + "HINT", + "ERROR" + ] + }, + "spring-boot.ls.problem.cron.FIELD": { + "type": "string", + "default": "ERROR", + "description": "Field", + "enum": [ + "IGNORE", + "INFO", + "WARNING", + "HINT", + "ERROR" + ] + } + } } ], "grammars": [