CRON parser. Syntax highlighting and validation inside java code.

This commit is contained in:
aboyko
2024-08-16 08:56:14 -04:00
parent 007ba15da2
commit 790d50f910
30 changed files with 4310 additions and 212 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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]
;

View File

@@ -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
;

View File

@@ -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] = "<INVALID>";
}
}
}
@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);
}
}
}

View File

@@ -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}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterCronExpression(CronParser.CronExpressionContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitCronExpression(CronParser.CronExpressionContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterSecondsElement(CronParser.SecondsElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitSecondsElement(CronParser.SecondsElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterMinutesElement(CronParser.MinutesElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitMinutesElement(CronParser.MinutesElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterHoursElement(CronParser.HoursElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitHoursElement(CronParser.HoursElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterDaysElement(CronParser.DaysElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitDaysElement(CronParser.DaysElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterMonthsElement(CronParser.MonthsElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitMonthsElement(CronParser.MonthsElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterDaysOfWeekElement(CronParser.DaysOfWeekElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitDaysOfWeekElement(CronParser.DaysOfWeekElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterCronElement(CronParser.CronElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitCronElement(CronParser.CronElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterRangeCronElement(CronParser.RangeCronElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitRangeCronElement(CronParser.RangeCronElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterTerminalCronElement(CronParser.TerminalCronElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitTerminalCronElement(CronParser.TerminalCronElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterPeriodicCronElement(CronParser.PeriodicCronElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitPeriodicCronElement(CronParser.PeriodicCronElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterRangeCronList(CronParser.RangeCronListContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitRangeCronList(CronParser.RangeCronListContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterNthDayOfWeekElement(CronParser.NthDayOfWeekElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitNthDayOfWeekElement(CronParser.NthDayOfWeekElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterLastDayOfWeekElement(CronParser.LastDayOfWeekElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitLastDayOfWeekElement(CronParser.LastDayOfWeekElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterNearestWeekDayToDayOfMonthElement(CronParser.NearestWeekDayToDayOfMonthElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitNearestWeekDayToDayOfMonthElement(CronParser.NearestWeekDayToDayOfMonthElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterLastDayOfMonthElement(CronParser.LastDayOfMonthElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitLastDayOfMonthElement(CronParser.LastDayOfMonthElementContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterWeekdayLiteral(CronParser.WeekdayLiteralContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitWeekdayLiteral(CronParser.WeekdayLiteralContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterMonthLiteral(CronParser.MonthLiteralContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitMonthLiteral(CronParser.MonthLiteralContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterEveryRule(ParserRuleContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitEveryRule(ParserRuleContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void visitTerminal(TerminalNode node) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void visitErrorNode(ErrorNode node) { }
}

View File

@@ -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);
}

View File

@@ -61,6 +61,11 @@
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>commons-util</artifactId>
<version>${dependencies.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>commons-yaml</artifactId>
@@ -196,6 +201,11 @@
<version>${dependencies.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>commons-util</artifactId>
<version>1.57.0-SNAPSHOT</version>
</dependency>
</dependencies>
<profiles>

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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"));
}

View File

@@ -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";
}

View File

@@ -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;
}
}

View File

@@ -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<DefaultLineTracker> 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<Boolean>() {
@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);
}
}
}

View File

@@ -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<Consumer<RecognitionException>> parseErrorHandler;
public CronSemanticTokens(Optional<Consumer<RecognitionException>> parseErrorHandler) {
this.parseErrorHandler = parseErrorHandler;
}
public CronSemanticTokens() {
this(Optional.empty());
}
@Override
public List<String> getTokenTypes() {
return List.of("operator", "number", "enum", "macro", "method");
}
@Override
public List<SemanticTokenData> computeTokens(String text, int initialOffset) {
CronLexer lexer = new CronLexer(CharStreams.fromString(text));
CommonTokenStream antlrTokens = new CommonTokenStream(lexer);
CronParser parser = new CronParser(antlrTokens);
Map<Token, String> semantics = new HashMap<>();
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
List<SemanticTokenData> 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;
}
}

View File

@@ -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);
}
};
}
}

View File

@@ -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<String> getTokenTypes() {
return tokensProvider.getTokenTypes();
}
@Override
public List<String> 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<SemanticTokenData> 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);
}
};
}
}

View File

@@ -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<? extends Lexer> 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<? extends Parser> parserClass, Class<? extends Lexer> 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<DefaultLineTracker> 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;
}

View File

@@ -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<SemanticTokenData> computeTokensForQueryExpression(SemanticTokensDataProvider provider, Expression valueExp) {
public static List<SemanticTokenData> computeTokensForExpression(SemanticTokensDataProvider provider, Expression valueExp) {
String query = null;
int offset = 0;
if (valueExp instanceof StringLiteral sl) {

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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"
}
]
}
]

View File

@@ -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<ReconcileProblem> 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());
}
}

View File

@@ -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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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
}
}

View File

@@ -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"
);
}
}

View File

@@ -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<SemanticTokenData> computeTokens(CompilationUnit cu) {
Collector<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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<SemanticTokenData> 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");
}
}

View File

@@ -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);

View File

@@ -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"
}
}
}
}

View File

@@ -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": [