diff --git a/bom/pom.xml b/bom/pom.xml index 961dd434c7e7..e187ab08f7ea 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -68,11 +68,6 @@ THE SOFTWARE. pom import - - antlr - antlr - 2.7.7 - args4j @@ -190,6 +185,11 @@ THE SOFTWARE. kxml2 2.3.0 + + org.antlr + antlr4-runtime + ${antlr.version} + org.apache.ant ant diff --git a/core/pom.xml b/core/pom.xml index 69b6a167f479..be76f35c8bbe 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -67,10 +67,6 @@ THE SOFTWARE. ${project.groupId} remoting - - antlr - antlr - args4j args4j @@ -278,6 +274,10 @@ THE SOFTWARE. net.sf.kxml kxml2 + + org.antlr + antlr4-runtime + org.apache.ant ant @@ -571,7 +571,6 @@ THE SOFTWARE. generate-sources - ${project.build.directory}/generated-sources/antlr ${project.build.directory}/generated-sources/localizer ${project.build.directory}/generated-sources/taglib-interface @@ -632,29 +631,15 @@ THE SOFTWARE. - org.codehaus.mojo - antlr-maven-plugin + org.antlr + antlr4-maven-plugin - cron + antlr - generate + antlr4 - - ${basedir}/src/main/grammar - crontab.g - - - - labelExpr - - generate - - - ${basedir}/src/main/grammar - labelExpr.g - diff --git a/core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 b/core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 new file mode 100644 index 000000000000..45e8ca01b233 --- /dev/null +++ b/core/src/main/antlr4/hudson/model/labels/LabelExpressionLexer.g4 @@ -0,0 +1,57 @@ +lexer grammar LabelExpressionLexer; + +AND + : '&&' + ; + +OR + : '||' + ; + +NOT + : '!' + ; + +IMPLIES + : '->' + ; + +IFF + : '<->' + ; + +LPAREN + : '(' + ; + +RPAREN + : ')' + ; + +fragment IDENTIFIER_PART + : ~ ('&' | '|' | '!' | '<' | '>' | '(' | ')' | ' ' | '\t' | '"' | '\'' | '-') + ; + +ATOM +/* + the real check of valid identifier happens in LabelAtom.get() + + https://www.antlr2.org/doc/lexer.html#usingexplicit + If we are seeing currently a '-', we check that the next char is not a '>' which will be a IMPLIES. + Otherwise the ATOM and the IMPLIES will collide and expr like a->b will just be parsed as ATOM (without spaces) +*/ + + : ( + { _input.LA(2) != '>' }? '-' | IDENTIFIER_PART)+ + ; + +WS + : (' ' | '\t')+ -> skip + ; + +STRINGLITERAL + : '"' ('\\' ('b' | 't' | 'n' | 'f' | 'r' | '"' | '\'' | '\\') /* escape */ + + | ~ ('\\' | '"' | '\r' | '\n'))* '"' + ; + diff --git a/core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 b/core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 new file mode 100644 index 000000000000..2ac7f2fedb51 --- /dev/null +++ b/core/src/main/antlr4/hudson/model/labels/LabelExpressionParser.g4 @@ -0,0 +1,56 @@ +parser grammar LabelExpressionParser; + +@ header +{ +import hudson.model.Label; +} + +options { tokenVocab = LabelExpressionLexer; } +// order of precedence is as per http://en.wikipedia.org/wiki/Logical_connective#Order_of_precedence + +expr returns[Label l] + : term1 + { $l=$term1.ctx.l; } EOF + ; + +term1 returns[Label l] locals[Label r] + : term2 + { $l=$term2.ctx.l; } (IFF term2 + { $r=$term2.ctx.l; $l=$l.iff($r); })* + ; + // (a->b)->c != a->(b->c) (for example in case of a=F,b=T,c=F) so don't allow chaining + +term2 returns[Label l] locals[Label r] + : term3 + { $l=$term3.ctx.l; } (IMPLIES term3 + { $r=$term3.ctx.l; $l=$l.implies($r); })? + ; + +term3 returns[Label l] locals[Label r] + : term4 + { $l=$term4.ctx.l; } (OR term4 + { $r=$term4.ctx.l; $l=$l.or($r); })* + ; + +term4 returns[Label l] locals[Label r] + : term5 + { $l=$term5.ctx.l; } (AND term5 + { $r=$term5.ctx.l; $l=$l.and($r); })* + ; + +term5 returns[Label l] + : term6 + { $l=$term6.ctx.l; } + | NOT term6 + { $l=$term6.ctx.l; $l=$l.not(); } + ; + +term6 returns[Label l] + : LPAREN term1 RPAREN + { $l=$term1.ctx.l ; $l=$l.paren(); } + | ATOM + { $l=LabelAtom.get($ATOM.getText()); } + | STRINGLITERAL + { $l=LabelAtom.get(hudson.util.QuotedStringTokenizer.unquote($STRINGLITERAL.getText())); } + ; + diff --git a/core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 b/core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 new file mode 100644 index 000000000000..a8e471c4c8f1 --- /dev/null +++ b/core/src/main/antlr4/hudson/scheduler/CrontabLexer.g4 @@ -0,0 +1,70 @@ +lexer grammar CrontabLexer; + +TOKEN + : ('0' .. '9')+ + ; + +WS + : (' ' | '\t')+ + ; + +MINUS + : '-' + ; + +STAR + : '*' + ; + +DIV + : '/' + ; + +OR + : ',' + ; + +AT + : '@' + ; + +H + : 'H' + ; + +LPAREN + : '(' + ; + +RPAREN + : ')' + ; + +YEARLY + : 'yearly' + ; + +ANNUALLY + : 'annually' + ; + +MONTHLY + : 'monthly' + ; + +WEEKLY + : 'weekly' + ; + +DAILY + : 'daily' + ; + +MIDNIGHT + : 'midnight' + ; + +HOURLY + : 'hourly' + ; + diff --git a/core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 b/core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 new file mode 100644 index 000000000000..4495afa62a34 --- /dev/null +++ b/core/src/main/antlr4/hudson/scheduler/CrontabParser.g4 @@ -0,0 +1,83 @@ +parser grammar CrontabParser; + + +options { tokenVocab = CrontabLexer; superClass = BaseParser; } +startRule[CronTab table] + : expr[0] + { $table.bits[0]=$expr.ctx.bits; } WS expr[1] + { $table.bits[1]=$expr.ctx.bits; } WS expr[2] + { $table.bits[2]=$expr.ctx.bits; } WS expr[3] + { $table.bits[3]=$expr.ctx.bits; } WS expr[4] + { $table.dayOfWeek=(int)$expr.ctx.bits; } EOF + | (AT ('yearly' + { + $table.set("H H H H *",getHashForTokens()); + } | 'annually' + { + $table.set("H H H H *",getHashForTokens()); + } | 'monthly' + { + $table.set("H H H * *",getHashForTokens()); + } | 'weekly' + { + $table.set("H H * * H",getHashForTokens()); + } | 'daily' + { + $table.set("H H * * *",getHashForTokens()); + } | 'midnight' + { + $table.set("H H(0-2) * * *",getHashForTokens()); + } | 'hourly' + { + $table.set("H * * * *",getHashForTokens()); + })) + ; + +expr[int field] returns[long bits=0] locals[long lhs, long rhs=0] + : term[field] + { $lhs = $term.ctx.bits; } (OR expr[field] + { $rhs = $expr.ctx.bits; })? + { + $bits = $lhs|$rhs; + } + ; + +term[int field] returns[long bits=0] locals[int d=NO_STEP, int s, int e] + : token + { $s=$token.ctx.value; } MINUS token + { $e=$token.ctx.value; } (DIV token + { $d=$token.ctx.value; })? + { + $bits = doRange($s,$e,$d,$field); + } + | token + { + rangeCheck($token.ctx.value,$field); + $bits = 1L<<$token.ctx.value; + } + | STAR (DIV token + { $d=$token.ctx.value; })? + { + $bits = doRange($d,$field); + } + | 'H' LPAREN token + { $s=$token.ctx.value; } MINUS token + { $e=$token.ctx.value; } RPAREN (DIV token + { $d=$token.ctx.value; })? + { + $bits = doHash($s,$e,$d,$field); + } + | 'H' (DIV token + { $d=$token.ctx.value; })? + { + $bits = doHash($d,$field); + } + ; + +token returns[int value=0] + : TOKEN + { + $value = Integer.parseInt($TOKEN.getText()); + } + ; + diff --git a/core/src/main/grammar/crontab.g b/core/src/main/grammar/crontab.g deleted file mode 100644 index 4ace2d3adf9c..000000000000 --- a/core/src/main/grammar/crontab.g +++ /dev/null @@ -1,164 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -header { - package hudson.scheduler; -} - -class CrontabParser extends Parser("BaseParser"); -options { - defaultErrorHandler=false; -} - -startRule [CronTab table] -throws ANTLRException -{ - long m,h,d,mnth,dow; -} - : m=expr[0] WS h=expr[1] WS d=expr[2] WS mnth=expr[3] WS dow=expr[4] EOF - { - table.bits[0]=m; - table.bits[1]=h; - table.bits[2]=d; - table.bits[3]=mnth; - table.dayOfWeek=(int)dow; - } - | ( AT - ( - "yearly" - { - table.set("H H H H *",getHashForTokens()); - } - | "annually" - { - table.set("H H H H *",getHashForTokens()); - } - | "monthly" - { - table.set("H H H * *",getHashForTokens()); - } - | "weekly" - { - table.set("H H * * H",getHashForTokens()); - } - | "daily" - { - table.set("H H * * *",getHashForTokens()); - } - | "midnight" - { - table.set("H H(0-2) * * *",getHashForTokens()); - } - | "hourly" - { - table.set("H * * * *",getHashForTokens()); - } - ) - ) - ; - -expr [int field] -returns [long bits=0] -throws ANTLRException -{ - long lhs,rhs=0; -} - : lhs=term[field] ("," rhs=expr[field])? - { - bits = lhs|rhs; - } - ; - -term [int field] -returns [long bits=0] -throws ANTLRException -{ - int d=NO_STEP,s,e,t; -} - : (token "-")=> s=token "-" e=token ( "/" d=token )? - { - bits = doRange(s,e,d,field); - } - | t=token - { - rangeCheck(t,field); - bits = 1L< "H" "(" s=token "-" e=token ")" ( "/" d=token )? - { - bits = doHash(s,e,d,field); - } - | "H" ( "/" d=token )? - { - bits = doHash(d,field); - } - ; - -token -returns [int value=0] - : t:TOKEN - { - value = Integer.parseInt(t.getText()); - } - ; - -class CrontabLexer extends Lexer; -options { - k=2; // I'm sure there's a better way to do this than using lookahead. ANTLR sucks... - defaultErrorHandler=false; -} - -TOKEN -options { - paraphrase="a number"; -} - : ('0'..'9')+ - ; - -WS -options { - paraphrase="space"; -} - : (' '|'\t')+ - ; - -MINUS: '-'; -STAR: '*'; -DIV: '/'; -OR: ','; -AT: '@'; -H: 'H'; -LPAREN: '('; -RPAREN: ')'; - -YEARLY: "yearly"; -ANNUALLY: "annually"; -MONTHLY: "monthly"; -WEEKLY: "weekly"; -DAILY: "daily"; -MIDNIGHT: "midnight"; -HOURLY: "hourly"; diff --git a/core/src/main/grammar/labelExpr.g b/core/src/main/grammar/labelExpr.g deleted file mode 100644 index c100f9e11cd9..000000000000 --- a/core/src/main/grammar/labelExpr.g +++ /dev/null @@ -1,133 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2010, InfraDNA, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -header { - package hudson.model.labels; - import hudson.model.Label; -} - -class LabelExpressionParser extends Parser; -options { - defaultErrorHandler=false; -} - -// order of precedence is as per http://en.wikipedia.org/wiki/Logical_connective#Order_of_precedence - -expr -returns [Label l] - : l=term1 EOF - ; - -term1 -returns [Label l] -{ Label r; } - : l=term2( IFF r=term2 {l=l.iff(r);} )* - ; - -// (a->b)->c != a->(b->c) (for example in case of a=F,b=T,c=F) so don't allow chaining -term2 -returns [Label l] -{ Label r; } - : l=term3( IMPLIES r=term3 {l=l.implies(r);} )? - ; - -term3 -returns [Label l] -{ Label r; } - : l=term4 ( OR r=term4 {l=l.or(r);} )* - ; - -term4 -returns [Label l] -{ Label r; } - : l=term5 ( AND r=term5 {l=l.and(r);} )* - ; - -term5 -returns [Label l] -{ Label x; } - : l=term6 - | NOT x=term6 - { l=x.not(); } - ; - -term6 -returns [Label l] -options { generateAmbigWarnings=false; } - : LPAREN l=term1 RPAREN - { l=l.paren(); } - | a:ATOM - { l=LabelAtom.get(a.getText()); } - | s:STRINGLITERAL - { l=LabelAtom.get(hudson.util.QuotedStringTokenizer.unquote(s.getText())); } - ; - -class LabelExpressionLexer extends Lexer; -options { - // There must be an options section to satisfy - // org.codehaus.mojo.antlr.metadata.MetadataExtracter#intrepret, even if it is empty. - - // https://www.antlr2.org/doc/lexer.html#Common_prefixes - // to prevent nondeterminism between IMPLIES and ATOM related to the first "-" - k=2; -} - -AND: "&&"; -OR: "||"; -NOT: "!"; -IMPLIES:"->"; -IFF: "<->"; -LPAREN: "("; -RPAREN: ")"; - -protected -IDENTIFIER_PART - : ~( '&' | '|' | '!' | '<' | '>' | '(' | ')' | ' ' | '\t' | '\"' | '\'' | '-' ) - ; - -ATOM -/* - the real check of valid identifier happens in LabelAtom.get() - - https://www.antlr2.org/doc/lexer.html#usingexplicit - If we are seeing currently a '-', we check that the next char is not a '>' which will be a IMPLIES. - Otherwise the ATOM and the IMPLIES will collide and expr like a->b will just be parsed as ATOM (without spaces) -*/ - : ( - { LA(2) != '>' }? '-' - | IDENTIFIER_PART - )+ - ; - -WS - : (' '|'\t')+ - { $setType(Token.SKIP); } - ; - -STRINGLITERAL - : '"' - ( '\\' ( 'b' | 't' | 'n' | 'f' | 'r' | '\"' | '\'' | '\\' ) /* escape */ - | ~( '\\' | '"' | '\r' | '\n' ) - )* - '"' - ; diff --git a/core/src/main/java/antlr/ANTLRException.java b/core/src/main/java/antlr/ANTLRException.java new file mode 100644 index 000000000000..77447338a64b --- /dev/null +++ b/core/src/main/java/antlr/ANTLRException.java @@ -0,0 +1,21 @@ +package antlr; + +/** + * This class is for binary compatibility for older plugins that import {@link ANTLRException}. + * + * @deprecated use {@link IllegalArgumentException} + */ +@Deprecated +public class ANTLRException extends IllegalArgumentException { + public ANTLRException(String message) { + super(message); + } + + public ANTLRException(String message, Throwable cause) { + super(message, cause); + } + + public ANTLRException(Throwable cause) { + super(cause); + } +} diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java index d3a1ea34119d..fa1a7777df7b 100644 --- a/core/src/main/java/hudson/model/AbstractProject.java +++ b/core/src/main/java/hudson/model/AbstractProject.java @@ -31,7 +31,6 @@ import static hudson.scm.PollingResult.BUILD_NOW; import static hudson.scm.PollingResult.NO_CHANGES; -import antlr.ANTLRException; import com.infradna.tool.bridge_method_injector.WithBridgeMethods; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; @@ -414,7 +413,7 @@ public String getAssignedLabelString() { try { Label.parseExpression(assignedNode); return assignedNode; - } catch (ANTLRException e) { + } catch (IllegalArgumentException e) { // must be old label or host name that includes whitespace or other unsafe chars return LabelAtom.escape(assignedNode); } diff --git a/core/src/main/java/hudson/model/Label.java b/core/src/main/java/hudson/model/Label.java index c285a1a28fda..43604d408aac 100644 --- a/core/src/main/java/hudson/model/Label.java +++ b/core/src/main/java/hudson/model/Label.java @@ -26,7 +26,6 @@ import static hudson.Util.fixNull; -import antlr.ANTLRException; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; @@ -56,7 +55,6 @@ import hudson.util.QuotedStringTokenizer; import hudson.util.VariableResolver; import java.io.Serializable; -import java.io.StringReader; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -68,6 +66,9 @@ import java.util.stream.StreamSupport; import jenkins.model.Jenkins; import jenkins.model.ModelObjectWithChildren; +import jenkins.util.antlr.JenkinsANTLRErrorListener; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.stapler.StaplerRequest; @@ -610,10 +611,18 @@ public static Label get(String l) { * Parses the expression into a label expression tree. * * TODO: replace this with a real parser later - */ - public static Label parseExpression(@NonNull String labelExpression) throws ANTLRException { - LabelExpressionLexer lexer = new LabelExpressionLexer(new StringReader(labelExpression)); - return new LabelExpressionParser(lexer).expr(); + * + * @param labelExpression the label expression to be parsed + * @throws IllegalArgumentException if the label expression cannot be parsed + */ + public static Label parseExpression(@NonNull String labelExpression) { + LabelExpressionLexer lexer = new LabelExpressionLexer(CharStreams.fromString(labelExpression)); + lexer.removeErrorListeners(); + lexer.addErrorListener(new JenkinsANTLRErrorListener()); + LabelExpressionParser parser = new LabelExpressionParser(new CommonTokenStream(lexer)); + parser.removeErrorListeners(); + parser.addErrorListener(new JenkinsANTLRErrorListener()); + return parser.expr().l; } /** diff --git a/core/src/main/java/hudson/model/labels/LabelExpression.java b/core/src/main/java/hudson/model/labels/LabelExpression.java index 40a7fc6f80db..20e666963883 100644 --- a/core/src/main/java/hudson/model/labels/LabelExpression.java +++ b/core/src/main/java/hudson/model/labels/LabelExpression.java @@ -24,7 +24,6 @@ package hudson.model.labels; -import antlr.ANTLRException; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -282,7 +281,7 @@ public static FormValidation validate(@Nullable String expression, @CheckForNull } try { Label.parseExpression(expression); - } catch (ANTLRException e) { + } catch (IllegalArgumentException e) { return FormValidation.error(e, Messages.LabelExpression_InvalidBooleanExpression(e.getMessage())); } final Jenkins j = Jenkins.get(); diff --git a/core/src/main/java/hudson/scheduler/BaseParser.java b/core/src/main/java/hudson/scheduler/BaseParser.java index 36a713101177..2c386cb3bdbc 100644 --- a/core/src/main/java/hudson/scheduler/BaseParser.java +++ b/core/src/main/java/hudson/scheduler/BaseParser.java @@ -24,20 +24,15 @@ package hudson.scheduler; -import antlr.ANTLRException; -import antlr.LLkParser; -import antlr.ParserSharedInputState; -import antlr.SemanticException; -import antlr.Token; -import antlr.TokenBuffer; -import antlr.TokenStream; -import antlr.TokenStreamException; import jenkins.util.SystemProperties; +import org.antlr.v4.runtime.InputMismatchException; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.TokenStream; /** * @author Kohsuke Kawaguchi */ -abstract class BaseParser extends LLkParser { +abstract class BaseParser extends Parser { // lower/upper bounds of fields (inclusive) static final int[] LOWER_BOUNDS = new int[] {0, 0, 1, 1, 0}; static final int[] UPPER_BOUNDS = new int[] {59, 23, 31, 12, 7}; @@ -47,28 +42,29 @@ abstract class BaseParser extends LLkParser { */ protected Hash hash = Hash.zero(); - protected BaseParser(int i) { - super(i); - } + /** + * Custom error message overriding ANTLR's {@link InputMismatchException} + */ + private String errorMessage; - protected BaseParser(ParserSharedInputState parserSharedInputState, int i) { - super(parserSharedInputState, i); + BaseParser(TokenStream input) { + super(input); } - protected BaseParser(TokenBuffer tokenBuffer, int i) { - super(tokenBuffer, i); + public void setHash(Hash hash) { + if (hash == null) hash = Hash.zero(); + this.hash = hash; } - protected BaseParser(TokenStream tokenStream, int i) { - super(tokenStream, i); + public String getErrorMessage() { + return errorMessage; } - public void setHash(Hash hash) { - if (hash == null) hash = Hash.zero(); - this.hash = hash; + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; } - protected long doRange(int start, int end, int step, int field) throws ANTLRException { + protected long doRange(int start, int end, int step, int field) { rangeCheck(start, field); rangeCheck(end, field); if (step <= 0) @@ -83,7 +79,7 @@ protected long doRange(int start, int end, int step, int field) throws ANTLRExce return bits; } - protected long doRange(int step, int field) throws ANTLRException { + protected long doRange(int step, int field) { return doRange(LOWER_BOUNDS[field], UPPER_BOUNDS[field], step, field); } @@ -94,14 +90,14 @@ protected long doRange(int step, int field) throws ANTLRException { * Increments. For example, 15 if "H/15". Or {@link #NO_STEP} to indicate * the special constant for "H" without the step value. */ - protected long doHash(int step, int field) throws ANTLRException { + protected long doHash(int step, int field) { int u = UPPER_BOUNDS[field]; if (field == 2) u = 28; // day of month can vary depending on month, so to make life simpler, just use [1,28] that's always safe if (field == 4) u = 6; // Both 0 and 7 of day of week are Sunday. For better distribution, limit upper bound to 6 return doHash(LOWER_BOUNDS[field], u, step, field); } - protected long doHash(int s, int e, int step, int field) throws ANTLRException { + protected long doHash(int s, int e, int step, int field) { rangeCheck(s, field); rangeCheck(e, field); if (step > e - s + 1) { @@ -124,20 +120,15 @@ protected long doHash(int s, int e, int step, int field) throws ANTLRException { } } - protected void rangeCheck(int value, int field) throws ANTLRException { + protected void rangeCheck(int value, int field) { if (value < LOWER_BOUNDS[field] || UPPER_BOUNDS[field] < value) { error(Messages.BaseParser_OutOfRange(value, LOWER_BOUNDS[field], UPPER_BOUNDS[field])); } } - private void error(String msg) throws TokenStreamException, SemanticException { - Token token = LT(0); - throw new SemanticException( - msg, - token.getFilename(), - token.getLine(), - token.getColumn() - ); + private void error(String msg) { + setErrorMessage(msg); + throw new InputMismatchException(this); } protected Hash getHashForTokens() { diff --git a/core/src/main/java/hudson/scheduler/CronTab.java b/core/src/main/java/hudson/scheduler/CronTab.java index 193f3a67944b..2c4b188a0de2 100644 --- a/core/src/main/java/hudson/scheduler/CronTab.java +++ b/core/src/main/java/hudson/scheduler/CronTab.java @@ -29,15 +29,16 @@ import static java.util.Calendar.MINUTE; import static java.util.Calendar.MONTH; -import antlr.ANTLRException; import edu.umd.cs.findbugs.annotations.CheckForNull; -import java.io.StringReader; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; +import jenkins.util.antlr.JenkinsANTLRErrorListener; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; /** * Table for driving scheduled tasks. @@ -67,53 +68,70 @@ public final class CronTab { */ private @CheckForNull String specTimezone; - public CronTab(String format) throws ANTLRException { + /** + * @param format the crontab entry to be parsed + * @throws IllegalArgumentException if the crontab entry cannot be parsed + */ + public CronTab(String format) { this(format, null); } - public CronTab(String format, Hash hash) throws ANTLRException { + /** + * @param format the crontab entry to be parsed + * @throws IllegalArgumentException if the crontab entry cannot be parsed + */ + public CronTab(String format, Hash hash) { this(format, 1, hash); } /** - * @deprecated as of 1.448 - * Use {@link #CronTab(String, int, Hash)} + * @param format the crontab entry to be parsed + * @throws IllegalArgumentException if the crontab entry cannot be parsed + * @deprecated use {@link #CronTab(String, int, Hash)} */ - @Deprecated - public CronTab(String format, int line) throws ANTLRException { + @Deprecated(since = "1.448") + public CronTab(String format, int line) { set(format, line, null); } /** + * @param format the crontab entry to be parsed * @param hash * Used to spread out token like "@daily". Null to preserve the legacy behaviour * of not spreading it out at all. + * @throws IllegalArgumentException if the crontab entry cannot be parsed */ - public CronTab(String format, int line, Hash hash) throws ANTLRException { + public CronTab(String format, int line, Hash hash) { this(format, line, hash, null); } /** + * @param format the crontab entry to be parsed * @param timezone * Used to schedule cron in a different timezone. Null to use the default system * timezone + * @throws IllegalArgumentException if the crontab entry cannot be parsed * @since 1.615 */ - public CronTab(String format, int line, Hash hash, @CheckForNull String timezone) throws ANTLRException { + public CronTab(String format, int line, Hash hash, @CheckForNull String timezone) { set(format, line, hash, timezone); } - private void set(String format, int line, Hash hash) throws ANTLRException { + private void set(String format, int line, Hash hash) { set(format, line, hash, null); } /** * @since 1.615 */ - private void set(String format, int line, Hash hash, String timezone) throws ANTLRException { - CrontabLexer lexer = new CrontabLexer(new StringReader(format)); + private void set(String format, int line, Hash hash, String timezone) { + CrontabLexer lexer = new CrontabLexer(CharStreams.fromString(format)); + lexer.removeErrorListeners(); + lexer.addErrorListener(new JenkinsANTLRErrorListener()); lexer.setLine(line); - CrontabParser parser = new CrontabParser(lexer); + CrontabParser parser = new CrontabParser(new CommonTokenStream(lexer)); + parser.removeErrorListeners(); + parser.addErrorListener(new JenkinsANTLRErrorListener(parser::getErrorMessage)); parser.setHash(hash); spec = format; specTimezone = timezone; @@ -465,7 +483,11 @@ public Calendar floor(Calendar cal) { } } - void set(String format, Hash hash) throws ANTLRException { + /** + * @param format the crontab entry to be parsed + * @throws IllegalArgumentException if the crontab entry cannot be parsed + */ + void set(String format, Hash hash) { set(format, 1, hash); } diff --git a/core/src/main/java/hudson/scheduler/CronTabList.java b/core/src/main/java/hudson/scheduler/CronTabList.java index 7a1b4760ff75..f9be65fe12d9 100644 --- a/core/src/main/java/hudson/scheduler/CronTabList.java +++ b/core/src/main/java/hudson/scheduler/CronTabList.java @@ -91,11 +91,19 @@ public String checkSanity() { return null; } - public static CronTabList create(@NonNull String format) throws ANTLRException { + /** + * @param format the crontab entry to be parsed + * @throws IllegalArgumentException if the crontab entry cannot be parsed + */ + public static CronTabList create(@NonNull String format) { return create(format, null); } - public static CronTabList create(@NonNull String format, Hash hash) throws ANTLRException { + /** + * @param format the crontab entry to be parsed + * @throws IllegalArgumentException if the crontab entry cannot be parsed + */ + public static CronTabList create(@NonNull String format, Hash hash) { Vector r = new Vector<>(); int lineNumber = 0; String timezone = null; @@ -119,8 +127,8 @@ public static CronTabList create(@NonNull String format, Hash hash) throws ANTLR continue; // ignorable line try { r.add(new CronTab(line, lineNumber, hash, timezone)); - } catch (ANTLRException e) { - throw new ANTLRException(Messages.CronTabList_InvalidInput(line, e.toString()), e); + } catch (IllegalArgumentException e) { + throw new ANTLRException(Messages.CronTabList_InvalidInput(line, e.getMessage()), e); } } diff --git a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java index adabf6e07dc3..6f09e8109973 100644 --- a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java +++ b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java @@ -27,7 +27,6 @@ import static hudson.Util.fixNull; import static java.util.logging.Level.INFO; -import antlr.ANTLRException; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import hudson.model.Computer; @@ -67,9 +66,12 @@ public class SimpleScheduledRetentionStrategy extends RetentionStrategy { private boolean ignorePostCommitHooks; @DataBoundConstructor - public SCMTrigger(String scmpoll_spec) throws ANTLRException { + public SCMTrigger(String scmpoll_spec) { super(scmpoll_spec); } @@ -125,7 +124,7 @@ public SCMTrigger(String scmpoll_spec) throws ANTLRException { * @deprecated since 2.21 */ @Deprecated - public SCMTrigger(String scmpoll_spec, boolean ignorePostCommitHooks) throws ANTLRException { + public SCMTrigger(String scmpoll_spec, boolean ignorePostCommitHooks) { super(scmpoll_spec); this.ignorePostCommitHooks = ignorePostCommitHooks; } diff --git a/core/src/main/java/hudson/triggers/TimerTrigger.java b/core/src/main/java/hudson/triggers/TimerTrigger.java index 054e3e63334f..bed439528659 100644 --- a/core/src/main/java/hudson/triggers/TimerTrigger.java +++ b/core/src/main/java/hudson/triggers/TimerTrigger.java @@ -27,7 +27,6 @@ import static hudson.Util.fixNull; -import antlr.ANTLRException; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import hudson.model.BuildableItem; @@ -54,7 +53,7 @@ public class TimerTrigger extends Trigger { @DataBoundConstructor - public TimerTrigger(@NonNull String spec) throws ANTLRException { + public TimerTrigger(@NonNull String spec) { super(spec); } @@ -95,10 +94,10 @@ public FormValidation doCheckSpec(@QueryParameter String value, @AncestorInPath updateValidationsForSanity(validations, ctl); updateValidationsForNextRun(validations, ctl); return FormValidation.aggregate(validations); - } catch (ANTLRException e) { + } catch (IllegalArgumentException e) { if (value.trim().indexOf('\n') == -1 && value.contains("**")) return FormValidation.error(Messages.TimerTrigger_MissingWhitespace()); - return FormValidation.error(e.getMessage()); + return FormValidation.error(e, e.getMessage()); } } diff --git a/core/src/main/java/hudson/triggers/Trigger.java b/core/src/main/java/hudson/triggers/Trigger.java index b95003aabc47..f7415597d1a7 100644 --- a/core/src/main/java/hudson/triggers/Trigger.java +++ b/core/src/main/java/hudson/triggers/Trigger.java @@ -25,7 +25,6 @@ package hudson.triggers; -import antlr.ANTLRException; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -100,7 +99,7 @@ public void start(J project, boolean newInstance) { } else { LOGGER.log(Level.WARNING, "The job {0} has a null crontab spec which is incorrect", job.getFullName()); } - } catch (ANTLRException e) { + } catch (IllegalArgumentException e) { // this shouldn't fail because we've already parsed stuff in the constructor, // so if it fails, use whatever 'tabs' that we already have. LOGGER.log(Level.WARNING, String.format("Failed to parse crontab spec %s in job %s", spec, project.getFullName()), e); @@ -170,8 +169,11 @@ public TriggerDescriptor getDescriptor() { * Creates a new {@link Trigger} that gets {@link #run() run} * periodically. This is useful when your trigger does * some polling work. + * + * @param cronTabSpec the crontab entry to be parsed + * @throws IllegalArgumentException if the crontab entry cannot be parsed */ - protected Trigger(@NonNull String cronTabSpec) throws ANTLRException { + protected Trigger(@NonNull String cronTabSpec) { this.spec = cronTabSpec; this.tabs = CronTabList.create(cronTabSpec); } @@ -196,7 +198,7 @@ public final String getSpec() { protected Object readResolve() throws ObjectStreamException { try { tabs = CronTabList.create(spec); - } catch (ANTLRException e) { + } catch (IllegalArgumentException e) { InvalidObjectException x = new InvalidObjectException(e.getMessage()); x.initCause(e); throw x; diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 4f92cc9fd408..eb9b8f58596a 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -42,7 +42,6 @@ import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; -import antlr.ANTLRException; import com.google.common.annotations.VisibleForTesting; import com.google.inject.Inject; import com.google.inject.Injector; @@ -2071,7 +2070,7 @@ public Label getLabel(String expr) { // For the record, this method creates temporary labels but there is a periodic task // calling "trimLabels" to remove unused labels running every 5 minutes. labels.putIfAbsent(expr, Label.parseExpression(expr)); - } catch (ANTLRException e) { + } catch (IllegalArgumentException e) { // laxly accept it as a single label atom for backward compatibility return getLabelAtom(expr); } diff --git a/core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java b/core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java new file mode 100644 index 000000000000..0d150b10b698 --- /dev/null +++ b/core/src/main/java/jenkins/util/antlr/JenkinsANTLRErrorListener.java @@ -0,0 +1,53 @@ +package jenkins.util.antlr; + +import antlr.ANTLRException; +import java.util.function.Supplier; +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +@Restricted(NoExternalUse.class) +public class JenkinsANTLRErrorListener extends BaseErrorListener { + + private final Supplier errorMessageSupplier; + + public JenkinsANTLRErrorListener() { + errorMessageSupplier = () -> null; + } + + public JenkinsANTLRErrorListener(Supplier errorMessageSupplier) { + this.errorMessageSupplier = errorMessageSupplier; + } + + @Override + public void syntaxError( + Recognizer recognizer, + Object offendingSymbol, + int line, + int charPositionInLine, + String msg, + RecognitionException e) { + String errorMessage = errorMessageSupplier.get(); + if (errorMessage != null) { + msg = errorMessage; + } + throw new ANTLRException(formatMessage(line, charPositionInLine, msg), e); + } + + private static String formatMessage(int line, int column, String message) { + StringBuilder sb = new StringBuilder(); + if (line != -1) { + sb.append("line "); + sb.append(line); + if (column != -1) { + sb.append(":"); + sb.append(column); + } + sb.append(": "); + } + sb.append(message); + return sb.toString(); + } +} diff --git a/core/src/test/java/hudson/scheduler/CronTabEventualityTest.java b/core/src/test/java/hudson/scheduler/CronTabEventualityTest.java index 56ae02a42ccd..73f1e55c5a77 100644 --- a/core/src/test/java/hudson/scheduler/CronTabEventualityTest.java +++ b/core/src/test/java/hudson/scheduler/CronTabEventualityTest.java @@ -2,7 +2,6 @@ import static org.junit.Assert.fail; -import antlr.ANTLRException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Calendar; @@ -42,7 +41,7 @@ public CronTabEventualityTest(String name, Hash hash) { @Test @Issue("JENKINS-12388") - public void testYearlyWillBeEventuallyTriggeredWithinOneYear() throws ANTLRException { + public void testYearlyWillBeEventuallyTriggeredWithinOneYear() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.YEAR, 1); checkEventuality(start, "@yearly", limit); @@ -50,56 +49,56 @@ public void testYearlyWillBeEventuallyTriggeredWithinOneYear() throws ANTLRExcep @Test @Issue("JENKINS-12388") - public void testAnnuallyWillBeEventuallyTriggeredWithinOneYear() throws ANTLRException { + public void testAnnuallyWillBeEventuallyTriggeredWithinOneYear() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.YEAR, 1); checkEventuality(start, "@annually", limit); } @Test - public void testMonthlyWillBeEventuallyTriggeredWithinOneMonth() throws ANTLRException { + public void testMonthlyWillBeEventuallyTriggeredWithinOneMonth() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.MONTH, 1); checkEventuality(start, "@monthly", limit); } @Test - public void testWeeklyWillBeEventuallyTriggeredWithinOneWeek() throws ANTLRException { + public void testWeeklyWillBeEventuallyTriggeredWithinOneWeek() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.WEEK_OF_YEAR, 1); checkEventuality(start, "@weekly", limit); } @Test - public void testDailyWillBeEventuallyTriggeredWithinOneDay() throws ANTLRException { + public void testDailyWillBeEventuallyTriggeredWithinOneDay() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.DAY_OF_MONTH, 1); checkEventuality(start, "@daily", limit); } @Test - public void testMidnightWillBeEventuallyTriggeredWithinOneDay() throws ANTLRException { + public void testMidnightWillBeEventuallyTriggeredWithinOneDay() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.DAY_OF_MONTH, 1); checkEventuality(start, "@midnight", limit); } @Test - public void testHourlyWillBeEventuallyTriggeredWithinOneHour() throws ANTLRException { + public void testHourlyWillBeEventuallyTriggeredWithinOneHour() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.HOUR, 1); checkEventuality(start, "@hourly", limit); } @Test - public void testFirstDayOfMonthWillBeEventuallyTriggeredWithinOneMonth() throws ANTLRException { + public void testFirstDayOfMonthWillBeEventuallyTriggeredWithinOneMonth() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.MONTH, 1); checkEventuality(start, "H H 1 * *", limit); } @Test - public void testFirstSundayOfMonthWillBeEventuallyTriggeredWithinOneMonthAndOneWeek() throws ANTLRException { + public void testFirstSundayOfMonthWillBeEventuallyTriggeredWithinOneMonthAndOneWeek() { Calendar start = new GregorianCalendar(2012, Calendar.JANUARY, 11, 22, 33); // Jan 11th 2012 22:33 Calendar limit = createLimit(start, Calendar.DAY_OF_MONTH, 31 + 7); // If both day of month and day of week are specified: @@ -108,7 +107,7 @@ public void testFirstSundayOfMonthWillBeEventuallyTriggeredWithinOneMonthAndOneW checkEventuality(start, "H H 1-7 * 0", limit); } - private void checkEventuality(Calendar start, String crontabFormat, Calendar limit) throws ANTLRException { + private void checkEventuality(Calendar start, String crontabFormat, Calendar limit) { CronTab cron = new CronTab(crontabFormat, hash); Calendar next = cron.ceil(start); if (next.after(limit)) { diff --git a/core/src/test/java/hudson/scheduler/CronTabTest.java b/core/src/test/java/hudson/scheduler/CronTabTest.java index d7f2c0f2e463..813ae6c375c5 100644 --- a/core/src/test/java/hudson/scheduler/CronTabTest.java +++ b/core/src/test/java/hudson/scheduler/CronTabTest.java @@ -25,6 +25,8 @@ package hudson.scheduler; import static java.util.Calendar.MONDAY; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; @@ -46,7 +48,7 @@ public class CronTabTest { @Test - public void test1() throws ANTLRException { + public void test1() { new CronTab("@yearly"); new CronTab("@weekly"); new CronTab("@midnight"); @@ -97,7 +99,7 @@ public void testCeil3_DoW7() throws Exception { */ @Issue("HUDSON-8656") // This is _not_ JENKINS-8656 @Test - public void testCeil4() throws ANTLRException { + public void testCeil4() { final Calendar cal = Calendar.getInstance(new Locale("de", "de")); cal.set(2011, Calendar.JANUARY, 16, 0, 0, 0); // Sunday, Jan 16th 2011, 00:00 final String cronStr = "0 23 * * 1-5"; // execute on weekdays @23:00 @@ -119,7 +121,7 @@ public void testCeil4() throws ANTLRException { */ @Issue("HUDSON-8656") // This is _not_ JENKINS-8656 @Test - public void testCeil5() throws ANTLRException { + public void testCeil5() { final Calendar cal = Calendar.getInstance(new Locale("de", "at")); cal.set(2011, Calendar.JANUARY, 16, 0, 0, 0); // Sunday, Jan 16th 2011, 00:00 final String cronStr = "0 23 * * 1-5"; // execute on weekdays @23:00 @@ -268,7 +270,8 @@ public int next(int n) { ANTLRException e = assertThrows(ANTLRException.class, () -> compare(new GregorianCalendar(2013, Calendar.MARCH, 21, 0, 0), new CronTab("H(0-3)/15 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, Calendar.MARCH, 21, 0, 0)))); - assertEquals("line 1:8: 15 is an invalid value. Must be within 1 and 4", e.toString()); + assertThat(e, instanceOf(IllegalArgumentException.class)); + assertEquals("line 1:9: 15 is an invalid value. Must be within 1 and 4", e.getMessage()); } @Test public void repeatedHash() throws Exception { @@ -288,12 +291,14 @@ public int next(int n) { @Test public void rangeBoundsCheckFailHour() { ANTLRException e = assertThrows(ANTLRException.class, () -> new CronTab("H H(12-24) * * *")); - assertEquals("line 1:10: 24 is an invalid value. Must be within 0 and 23", e.toString()); + assertThat(e, instanceOf(IllegalArgumentException.class)); + assertEquals("line 1:10: 24 is an invalid value. Must be within 0 and 23", e.getMessage()); } @Test public void rangeBoundsCheckFailMinute() { ANTLRException e = assertThrows(ANTLRException.class, () -> new CronTab("H(33-66) * * * *")); - assertEquals("line 1:8: 66 is an invalid value. Must be within 0 and 59", e.toString()); + assertThat(e, instanceOf(IllegalArgumentException.class)); + assertEquals("line 1:8: 66 is an invalid value. Must be within 0 and 59", e.getMessage()); } @Issue("JENKINS-9283") diff --git a/core/src/test/java/hudson/triggers/TimerTriggerTest.java b/core/src/test/java/hudson/triggers/TimerTriggerTest.java index 88710db588f1..3d2a6ab01007 100644 --- a/core/src/test/java/hudson/triggers/TimerTriggerTest.java +++ b/core/src/test/java/hudson/triggers/TimerTriggerTest.java @@ -24,7 +24,6 @@ package hudson.triggers; -import antlr.ANTLRException; import hudson.scheduler.CronTabList; import hudson.scheduler.Hash; import java.util.TimeZone; @@ -38,7 +37,7 @@ public class TimerTriggerTest { @Issue("JENKINS-29790") @Test - public void testNoNPE() throws ANTLRException { + public void testNoNPE() { new TimerTrigger("").run(); } diff --git a/pom.xml b/pom.xml index 9439db89b4e8..d90f8d83244e 100644 --- a/pom.xml +++ b/pom.xml @@ -96,6 +96,7 @@ THE SOFTWARE. ${maven.multiModuleProjectDirectory}/src/spotbugs/spotbugs-excludes.xml 1.27 + 4.11.1 1.23 2.27.2 6.6 @@ -297,9 +298,9 @@ THE SOFTWARE. ${bridge-method-injector.version} - org.codehaus.mojo - antlr-maven-plugin - 2.2 + org.antlr + antlr4-maven-plugin + ${antlr.version} org.apache.maven.plugins @@ -383,6 +384,9 @@ THE SOFTWARE. spotless-maven-plugin ${spotless.version} + + + diff --git a/src/spotbugs/spotbugs-excludes.xml b/src/spotbugs/spotbugs-excludes.xml index d57cc5fb6aff..0971834a231e 100644 --- a/src/spotbugs/spotbugs-excludes.xml +++ b/src/spotbugs/spotbugs-excludes.xml @@ -22,11 +22,21 @@ + + + + + + + + + + @@ -38,6 +48,11 @@ + + + + +