Improve IfFeaturePredicateVisitor 02/77002/1
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 11 Aug 2018 00:10:23 +0000 (02:10 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Mon, 15 Oct 2018 15:32:09 +0000 (17:32 +0200)
We need to detect parser errors and not print them to stderr,
create an abstract class with the validation functionality
and use it when parsing an if-feature argument.

Change-Id: I63ad241bcfeeda7cb05f1472f593167d5ce08404
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
(cherry picked from commit 41cd74eff8a48f5437e76990319c97fd7bdd5df2)

yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/antlr/AbstractParserErrorListener.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/antlr/SourceExceptionParser.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/if_feature/IfFeaturePredicateVisitor.java

diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/antlr/AbstractParserErrorListener.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/antlr/AbstractParserErrorListener.java
new file mode 100644 (file)
index 0000000..1e83d6e
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  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 http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.rfc7950.antlr;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.annotations.Beta;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import javax.annotation.concurrent.NotThreadSafe;
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Mutable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract base type for converting ANTLRErrorListener errors to Exceptions.
+ *
+ * @param <E> Reported exception type
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+@NotThreadSafe
+public abstract class AbstractParserErrorListener<E extends Exception> extends BaseErrorListener implements Mutable {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractParserErrorListener.class);
+
+    private final Collection<E> exceptions = new ArrayList<>();
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final void syntaxError(final @Nullable Recognizer<?, ?> recognizer, final @Nullable Object offendingSymbol,
+            final int line, final int charPositionInLine, final @Nullable String msg,
+            final @Nullable RecognitionException e) {
+        LOG.debug("Syntax error at {}:{}: {}", line, charPositionInLine, msg, e);
+        exceptions.add(verifyNotNull(createException(verifyNotNull(recognizer), offendingSymbol, line,
+            charPositionInLine, verifyNotNull(msg), e)));
+    }
+
+    public final void validate() throws E {
+        if (!exceptions.isEmpty()) {
+            final Iterator<E> it = exceptions.iterator();
+            final E exception = it.next();
+            it.forEachRemaining(exception::addSuppressed);
+            throw exception;
+        }
+    }
+
+    protected abstract E createException(Recognizer<?, ?> recognizer, @Nullable Object offendingSymbol, int line,
+            int charPositionInLine, String msg, @Nullable RecognitionException cause);
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/antlr/SourceExceptionParser.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/antlr/SourceExceptionParser.java
new file mode 100644 (file)
index 0000000..3867dad
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  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 http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.rfc7950.antlr;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.function.Supplier;
+import javax.annotation.concurrent.NotThreadSafe;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+
+/**
+ * Utility class for converting ANTLRErrorListener errors to SourceExceptions.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+@NotThreadSafe
+public final class SourceExceptionParser {
+    private static final class Listener extends AbstractParserErrorListener<SourceException> {
+        private final StatementSourceReference ref;
+
+        private Listener(final StatementSourceReference ref) {
+            this.ref = requireNonNull(ref);
+        }
+
+        @Override
+        protected SourceException createException(final Recognizer<?, ?> recognizer,
+                final @Nullable Object offendingSymbol, final int line, final int charPositionInLine,
+                final String msg, final @Nullable RecognitionException cause) {
+            return new SourceException(ref, cause, "%s at %s:%s", msg, line, charPositionInLine);
+        }
+    }
+
+    private SourceExceptionParser() {
+
+    }
+
+    /**
+     * Parse a Recognizer extracting its root item.
+     *
+     * @param recognizer Recognizer to use
+     * @param parseMethod Root item extractor method
+     * @param ref Source reference
+     * @return Parsed item
+     * @throws NullPointerException if any argument is null
+     * @throws SourceException if a parser error occurs
+     */
+    public static <T> T parse(final Recognizer<?, ?> recognizer, final Supplier<T> parseMethod,
+            final StatementSourceReference ref) {
+        final Listener listener = new Listener(ref);
+        recognizer.removeErrorListeners();
+        recognizer.addErrorListener(listener);
+
+        final T ret = parseMethod.get();
+        listener.validate();
+        return ret;
+    }
+}
\ No newline at end of file
index a286cebe2ccd5b7ef97eb60cde0edb9f272ff77a..bcabd02f7ad3d60bd273b08c602b5d31704fbe20 100644 (file)
@@ -13,6 +13,8 @@ import java.util.Set;
 import java.util.function.Predicate;
 import org.antlr.v4.runtime.CharStreams;
 import org.antlr.v4.runtime.CommonTokenStream;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.antlrv4.code.gen.IfFeatureExpressionLexer;
 import org.opendaylight.yangtools.antlrv4.code.gen.IfFeatureExpressionParser;
 import org.opendaylight.yangtools.antlrv4.code.gen.IfFeatureExpressionParser.Identifier_ref_argContext;
@@ -21,10 +23,12 @@ import org.opendaylight.yangtools.antlrv4.code.gen.IfFeatureExpressionParser.If_
 import org.opendaylight.yangtools.antlrv4.code.gen.IfFeatureExpressionParser.If_feature_termContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.IfFeatureExpressionParserBaseVisitor;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.parser.rfc7950.antlr.SourceExceptionParser;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 
+@NonNullByDefault
 final class IfFeaturePredicateVisitor extends IfFeatureExpressionParserBaseVisitor<Predicate<Set<QName>>> {
     private final StmtContext<?, ?, ?> stmtCtx;
 
@@ -33,33 +37,28 @@ final class IfFeaturePredicateVisitor extends IfFeatureExpressionParserBaseVisit
     }
 
     static Predicate<Set<QName>> parseIfFeatureExpression(final StmtContext<?, ?, ?> ctx, final String value) {
-        final IfFeatureExpressionLexer lexer = new IfFeatureExpressionLexer(CharStreams.fromString(value));
-        final CommonTokenStream tokens = new CommonTokenStream(lexer);
-        final IfFeatureExpressionParser parser = new IfFeatureExpressionParser(tokens);
-
-        return new IfFeaturePredicateVisitor(ctx).visit(parser.if_feature_expr());
+        final IfFeatureExpressionParser parser = new IfFeatureExpressionParser(new CommonTokenStream(
+            new IfFeatureExpressionLexer(CharStreams.fromString(value))));
+        return new IfFeaturePredicateVisitor(ctx).visit(SourceExceptionParser.parse(parser, parser::if_feature_expr,
+            ctx.getStatementSourceReference()));
     }
 
     @Override
-    public Predicate<Set<QName>> visitIf_feature_expr(final If_feature_exprContext ctx) {
-        if (ctx.if_feature_expr() == null) {
-            return visitIf_feature_term(ctx.if_feature_term());
-        }
-
-        return visitIf_feature_term(ctx.if_feature_term()).or(visitIf_feature_expr(ctx.if_feature_expr()));
+    public Predicate<Set<QName>> visitIf_feature_expr(final @Nullable If_feature_exprContext ctx) {
+        final Predicate<Set<QName>> term = visitIf_feature_term(ctx.if_feature_term());
+        final If_feature_exprContext expr = ctx.if_feature_expr();
+        return expr == null ? term : term.or(visitIf_feature_expr(expr));
     }
 
     @Override
-    public Predicate<Set<QName>> visitIf_feature_term(final If_feature_termContext ctx) {
-        if (ctx.if_feature_term() == null) {
-            return visitIf_feature_factor(ctx.if_feature_factor());
-        }
-
-        return visitIf_feature_factor(ctx.if_feature_factor()).and(visitIf_feature_term(ctx.if_feature_term()));
+    public Predicate<Set<QName>> visitIf_feature_term(final @Nullable If_feature_termContext ctx) {
+        final Predicate<Set<QName>> factor = visitIf_feature_factor(ctx.if_feature_factor());
+        final If_feature_termContext term = ctx.if_feature_term();
+        return term == null ? factor : factor.and(visitIf_feature_term(term));
     }
 
     @Override
-    public Predicate<Set<QName>> visitIf_feature_factor(final If_feature_factorContext ctx) {
+    public Predicate<Set<QName>> visitIf_feature_factor(final @Nullable If_feature_factorContext ctx) {
         if (ctx.if_feature_expr() != null) {
             return visitIf_feature_expr(ctx.if_feature_expr());
         } else if (ctx.if_feature_factor() != null) {
@@ -73,7 +72,7 @@ final class IfFeaturePredicateVisitor extends IfFeatureExpressionParserBaseVisit
     }
 
     @Override
-    public Predicate<Set<QName>> visitIdentifier_ref_arg(final Identifier_ref_argContext ctx) {
+    public Predicate<Set<QName>> visitIdentifier_ref_arg(final @Nullable Identifier_ref_argContext ctx) {
         final QName featureQName = StmtContextUtils.parseNodeIdentifier(stmtCtx, ctx.getText());
         return setQNames -> setQNames.contains(featureQName);
     }