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