Attach error listeners to lexers
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / antlr / SourceExceptionParser.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.parser.rfc7950.antlr;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import java.util.function.Supplier;
14 import javax.annotation.concurrent.NotThreadSafe;
15 import org.antlr.v4.runtime.Lexer;
16 import org.antlr.v4.runtime.Parser;
17 import org.antlr.v4.runtime.RecognitionException;
18 import org.antlr.v4.runtime.Recognizer;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
22 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
23
24 /**
25  * Utility class for converting ANTLRErrorListener errors to SourceExceptions.
26  *
27  * @author Robert Varga
28  */
29 @Beta
30 @NonNullByDefault
31 @NotThreadSafe
32 public final class SourceExceptionParser {
33     private static final class Listener extends AbstractParserErrorListener<SourceException> {
34         private final StatementSourceReference ref;
35
36         private Listener(final StatementSourceReference ref) {
37             this.ref = requireNonNull(ref);
38         }
39
40         @Override
41         protected SourceException createException(final Recognizer<?, ?> recognizer,
42                 final @Nullable Object offendingSymbol, final int line, final int charPositionInLine,
43                 final String msg, final @Nullable RecognitionException cause) {
44             return new SourceException(ref, cause, "%s at %s:%s", msg, line, charPositionInLine);
45         }
46     }
47
48     private SourceExceptionParser() {
49
50     }
51
52     /**
53      * Parse a Recognizer extracting its root item.
54      *
55      * @param recognizer Recognizer to use
56      * @param parseMethod Root item extractor method
57      * @param ref Source reference
58      * @return Parsed item
59      * @throws NullPointerException if any argument is null
60      * @throws SourceException if a parser error occurs
61      */
62     public static <T> T parse(final Recognizer<?, ?> recognizer, final Supplier<T> parseMethod,
63             final StatementSourceReference ref) {
64         final Listener listener = new Listener(ref);
65         recognizer.removeErrorListeners();
66         recognizer.addErrorListener(listener);
67
68         final T ret = parseMethod.get();
69         listener.validate();
70         return ret;
71     }
72
73     /**
74      * Use a Lexer/Parser pair extracting the parser's root item.
75      *
76      * @param lexer lexer to use
77      * @param parser parser to use
78      * @param parseMethod Root item extractor method
79      * @param ref Source reference
80      * @return Parsed item
81      * @throws NullPointerException if any argument is null
82      * @throws SourceException if a parser error occurs
83      */
84     public static <T> T parse(final Lexer lexer, final Parser parser, final Supplier<T> parseMethod,
85             final StatementSourceReference ref) {
86         final Listener listener = new Listener(ref);
87         lexer.removeErrorListeners();
88         lexer.addErrorListener(listener);
89         parser.removeErrorListeners();
90         parser.addErrorListener(listener);
91
92         final T ret = parseMethod.get();
93         listener.validate();
94         return ret;
95     }
96 }