Rework keyword parser rule
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / repo / YangStatementStreamSource.java
1 /*
2  * Copyright (c) 2017 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.repo;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Verify.verifyNotNull;
12 import static java.util.Objects.requireNonNull;
13
14 import com.google.common.annotations.Beta;
15 import com.google.common.collect.ImmutableList;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import org.antlr.v4.runtime.CharStreams;
19 import org.antlr.v4.runtime.CommonTokenStream;
20 import org.antlr.v4.runtime.ParserRuleContext;
21 import org.antlr.v4.runtime.tree.ErrorNode;
22 import org.antlr.v4.runtime.tree.ParseTreeListener;
23 import org.antlr.v4.runtime.tree.ParseTreeWalker;
24 import org.antlr.v4.runtime.tree.TerminalNode;
25 import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.common.QNameModule;
28 import org.opendaylight.yangtools.yang.common.YangVersion;
29 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
30 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
31 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
32 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
33 import org.opendaylight.yangtools.yang.parser.antlr.YangStatementLexer;
34 import org.opendaylight.yangtools.yang.parser.antlr.YangStatementParser;
35 import org.opendaylight.yangtools.yang.parser.antlr.YangStatementParser.FileContext;
36 import org.opendaylight.yangtools.yang.parser.antlr.YangStatementParser.KeywordContext;
37 import org.opendaylight.yangtools.yang.parser.antlr.YangStatementParser.StatementContext;
38 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
39 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
40 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
41 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
42 import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
43 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
44
45 /**
46  * This class represents implementation of StatementStreamSource in order to emit YANG statements using supplied
47  * StatementWriter.
48  *
49  * @author Robert Varga
50  */
51 @Beta
52 public final class YangStatementStreamSource extends AbstractIdentifiable<SourceIdentifier>
53         implements StatementStreamSource {
54     private static final ParseTreeListener MAKE_IMMUTABLE_LISTENER = new ParseTreeListener() {
55         @Override
56         public void enterEveryRule(final ParserRuleContext ctx) {
57             // No-op
58         }
59
60         @Override
61         public void exitEveryRule(final ParserRuleContext ctx) {
62             ctx.children = ctx.children == null ? ImmutableList.of() : ImmutableList.copyOf(ctx.children);
63         }
64
65         @Override
66         public void visitTerminal(final TerminalNode node) {
67             // No-op
68         }
69
70         @Override
71         public void visitErrorNode(final ErrorNode node) {
72             // No-op
73         }
74     };
75
76     private final StatementContext context;
77     private final String sourceName;
78
79     private YangStatementStreamSource(final SourceIdentifier identifier,  final StatementContext context,
80             final String sourceName) {
81         super(identifier);
82         this.context = requireNonNull(context);
83         this.sourceName = sourceName;
84     }
85
86     /**
87      * Create a {@link YangStatementStreamSource} for a {@link YangTextSchemaSource}.
88      *
89      * @param source YangTextSchemaSource, must not be null
90      * @return A new {@link YangStatementStreamSource}
91      * @throws IOException When we fail to read the source
92      * @throws YangSyntaxErrorException If the source fails basic parsing
93      */
94     public static YangStatementStreamSource create(final YangTextSchemaSource source) throws IOException,
95             YangSyntaxErrorException {
96         final StatementContext context;
97         try (InputStream stream = source.openStream()) {
98             context = parseYangSource(source.getIdentifier(), stream);
99         }
100
101         return new YangStatementStreamSource(source.getIdentifier(), context, source.getSymbolicName().orElse(null));
102     }
103
104     /**
105      * Create a {@link YangStatementStreamSource} for a {@link ASTSchemaSource}.
106      *
107      * @param source YangTextSchemaSource, must not be null
108      * @return A new {@link YangStatementStreamSource}
109      */
110     public static YangStatementStreamSource create(final ASTSchemaSource source) {
111         final ParserRuleContext ast = source.getAST();
112         checkArgument(ast instanceof StatementContext,
113                 "Unsupported context class %s for source %s", ast.getClass(), source.getIdentifier());
114         return create(source.getIdentifier(), (StatementContext) ast, source.getSymbolicName().orElse(null));
115     }
116
117     public static YangStatementStreamSource create(final SourceIdentifier identifier, final StatementContext context,
118         final String symbolicName) {
119         return new YangStatementStreamSource(identifier, context, symbolicName);
120     }
121
122     @Override
123     public void writePreLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
124         new StatementContextVisitor(sourceName, writer, stmtDef, null, YangVersion.VERSION_1).visit(context);
125     }
126
127     @Override
128     public void writeLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
129             final PrefixToModule preLinkagePrefixes, final YangVersion yangVersion) {
130         new StatementContextVisitor(sourceName, writer, stmtDef, preLinkagePrefixes, yangVersion) {
131             @Override
132             StatementDefinition resolveStatement(final QNameModule module, final String localName) {
133                 return stmtDef.getByNamespaceAndLocalName(module.getNamespace(), localName);
134             }
135         }.visit(context);
136     }
137
138     @Override
139     public void writeLinkageAndStatementDefinitions(final StatementWriter writer,
140             final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes, final YangVersion yangVersion) {
141         new StatementContextVisitor(sourceName, writer, stmtDef, prefixes, yangVersion).visit(context);
142     }
143
144     @Override
145     public void writeFull(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
146             final PrefixToModule prefixes, final YangVersion yangVersion) {
147         new StatementContextVisitor(sourceName, writer, stmtDef, prefixes, yangVersion) {
148             @Override
149             QName getValidStatementDefinition(final KeywordContext keyword, final StatementSourceReference ref) {
150                 final QName ret = super.getValidStatementDefinition(keyword, ref);
151                 if (ret == null) {
152                     throw new SourceException(ref, "%s is not a YANG statement or use of extension.",
153                         keyword.getText());
154                 }
155                 return ret;
156             }
157         }.visit(context);
158     }
159
160     public ParserRuleContext getYangAST() {
161         return context;
162     }
163
164     private static StatementContext parseYangSource(final SourceIdentifier source, final InputStream stream)
165             throws IOException, YangSyntaxErrorException {
166         final YangStatementLexer lexer = new YangStatementLexer(CharStreams.fromStream(stream));
167         final YangStatementParser parser = new YangStatementParser(new CommonTokenStream(lexer));
168         // disconnect from console error output
169         lexer.removeErrorListeners();
170         parser.removeErrorListeners();
171
172         final YangErrorListener errorListener = new YangErrorListener(source);
173         lexer.addErrorListener(errorListener);
174         parser.addErrorListener(errorListener);
175
176         final FileContext result = parser.file();
177         errorListener.validate();
178
179         // Walk the resulting tree and replace each children with an immutable list, lowering memory requirements
180         // and making sure the resulting tree will not get accidentally modified. An alternative would be to use
181         // org.antlr.v4.runtime.Parser.TrimToSizeListener, but that does not make the tree immutable.
182         ParseTreeWalker.DEFAULT.walk(MAKE_IMMUTABLE_LISTENER, result);
183
184         return verifyNotNull(result.statement());
185     }
186 }