Turn YangStatementParserListenerImpl into a visitor
[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
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.MoreObjects;
14 import com.google.common.base.Preconditions;
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.antlrv4.code.gen.YangStatementLexer;
26 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser;
27 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
28 import org.opendaylight.yangtools.yang.common.YangVersion;
29 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
30 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
31 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
32 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
33 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
34 import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
35 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
36
37 /**
38  * This class represents implementation of StatementStreamSource in order to emit YANG statements using supplied
39  * StatementWriter.
40  *
41  * @author Robert Varga
42  */
43 @Beta
44 public final class YangStatementStreamSource implements StatementStreamSource {
45     private static final ParseTreeListener MAKE_IMMUTABLE_LISTENER = new ParseTreeListener() {
46         @Override
47         public void enterEveryRule(final ParserRuleContext ctx) {
48             // No-op
49         }
50
51         @Override
52         public void exitEveryRule(final ParserRuleContext ctx) {
53             ctx.children = ctx.children == null ? ImmutableList.of() : ImmutableList.copyOf(ctx.children);
54         }
55
56         @Override
57         public void visitTerminal(final TerminalNode node) {
58             // No-op
59         }
60
61         @Override
62         public void visitErrorNode(final ErrorNode node) {
63             // No-op
64         }
65     };
66
67     private final SourceIdentifier identifier;
68     private final StatementContext context;
69     private final String sourceName;
70
71     private YangStatementStreamSource(final SourceIdentifier identifier,  final StatementContext context,
72             final String sourceName) {
73         this.identifier = Preconditions.checkNotNull(identifier);
74         this.context = Preconditions.checkNotNull(context);
75         this.sourceName = sourceName;
76     }
77
78     /**
79      * Create a {@link YangStatementStreamSource} for a {@link YangTextSchemaSource}.
80      *
81      * @param source YangTextSchemaSource, must not be null
82      * @return A new {@link YangStatementStreamSource}
83      * @throws IOException When we fail to read the source
84      * @throws YangSyntaxErrorException If the source fails basic parsing
85      */
86     public static YangStatementStreamSource create(final YangTextSchemaSource source) throws IOException,
87             YangSyntaxErrorException {
88         final StatementContext context;
89         try (InputStream stream = source.openStream()) {
90             context = parseYangSource(source.getIdentifier(), stream);
91         }
92
93         return new YangStatementStreamSource(source.getIdentifier(), context, source.getSymbolicName().orElse(null));
94     }
95
96     /**
97      * Create a {@link YangStatementStreamSource} for a {@link ASTSchemaSource}.
98      *
99      * @param source YangTextSchemaSource, must not be null
100      * @return A new {@link YangStatementStreamSource}
101      */
102     public static YangStatementStreamSource create(final ASTSchemaSource source) {
103         final ParserRuleContext ast = source.getAST();
104         checkArgument(ast instanceof StatementContext,
105                 "Unsupported context class %s for source %s", ast.getClass(), source.getIdentifier());
106         return create(source.getIdentifier(), (StatementContext) ast, source.getSymbolicName().orElse(null));
107     }
108
109     public static YangStatementStreamSource create(final SourceIdentifier identifier, final StatementContext context,
110         final String symbolicName) {
111         return new YangStatementStreamSource(identifier, context, symbolicName);
112     }
113
114     @Override
115     public void writePreLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
116         new StatementContextVisitor.Loose(sourceName, writer, stmtDef).visit(context);
117     }
118
119     @Override
120     public void writeLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
121             final PrefixToModule preLinkagePrefixes) {
122         writeLinkage(writer, stmtDef, preLinkagePrefixes, YangVersion.VERSION_1);
123     }
124
125     @Override
126     public void writeLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
127             final PrefixToModule preLinkagePrefixes, final YangVersion yangVersion) {
128         new StatementContextVisitor.Loose(sourceName, writer, stmtDef, preLinkagePrefixes, yangVersion).visit(context);
129     }
130
131     @Override
132     public void writeLinkageAndStatementDefinitions(final StatementWriter writer,
133             final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes) {
134         writeLinkageAndStatementDefinitions(writer, stmtDef, prefixes, YangVersion.VERSION_1);
135     }
136
137     @Override
138     public void writeLinkageAndStatementDefinitions(final StatementWriter writer,
139             final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes, final YangVersion yangVersion) {
140         new StatementContextVisitor.Loose(sourceName, writer, stmtDef, prefixes, yangVersion).visit(context);
141     }
142
143     @Override
144     public void writeFull(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
145             final PrefixToModule prefixes) {
146         writeFull(writer, stmtDef, prefixes, YangVersion.VERSION_1);
147     }
148
149     @Override
150     public void writeFull(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
151             final PrefixToModule prefixes, final YangVersion yangVersion) {
152         new StatementContextVisitor.Strict(sourceName, writer, stmtDef, prefixes, yangVersion).visit(context);
153     }
154
155     @Override
156     public SourceIdentifier getIdentifier() {
157         return identifier;
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 CommonTokenStream tokens = new CommonTokenStream(lexer);
168         final YangStatementParser parser = new YangStatementParser(tokens);
169         //disconnect from console error output
170         parser.removeErrorListeners();
171
172         final YangErrorListener errorListener = new YangErrorListener(source);
173         parser.addErrorListener(errorListener);
174
175         final StatementContext result = parser.statement();
176         errorListener.validate();
177
178         // Walk the resulting tree and replace each children with an immutable list, lowering memory requirements
179         // and making sure the resulting tree will not get accidentally modified. An alternative would be to use
180         // org.antlr.v4.runtime.Parser.TrimToSizeListener, but that does not make the tree immutable.
181         ParseTreeWalker.DEFAULT.walk(MAKE_IMMUTABLE_LISTENER, result);
182
183         return result;
184     }
185
186     @Override
187     public String toString() {
188         return MoreObjects.toStringHelper(this).add("identifier", getIdentifier()).toString();
189     }
190 }