YANGTOOLS-706: Split out yang-parser-rfc7950
[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 com.google.common.annotations.Beta;
11 import com.google.common.base.MoreObjects;
12 import com.google.common.base.Preconditions;
13 import com.google.common.collect.ImmutableList;
14 import java.io.IOException;
15 import java.io.InputStream;
16 import org.antlr.v4.runtime.CharStreams;
17 import org.antlr.v4.runtime.CommonTokenStream;
18 import org.antlr.v4.runtime.ParserRuleContext;
19 import org.antlr.v4.runtime.tree.ErrorNode;
20 import org.antlr.v4.runtime.tree.ParseTreeListener;
21 import org.antlr.v4.runtime.tree.ParseTreeWalker;
22 import org.antlr.v4.runtime.tree.TerminalNode;
23 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementLexer;
24 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser;
25 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
26 import org.opendaylight.yangtools.yang.common.YangVersion;
27 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
28 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
29 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
30 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
31 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
32 import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
33 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
34
35 /**
36  * This class represents implementation of StatementStreamSource in order to emit YANG statements using supplied
37  * StatementWriter.
38  *
39  * @author Robert Varga
40  */
41 @Beta
42 public final class YangStatementStreamSource implements StatementStreamSource {
43     private static final ParseTreeListener MAKE_IMMUTABLE_LISTENER = new ParseTreeListener() {
44         @Override
45         public void enterEveryRule(final ParserRuleContext ctx) {
46             // No-op
47         }
48
49         @Override
50         public void exitEveryRule(final ParserRuleContext ctx) {
51             ctx.children = ctx.children == null ? ImmutableList.of() : ImmutableList.copyOf(ctx.children);
52         }
53
54         @Override
55         public void visitTerminal(final TerminalNode node) {
56             // No-op
57         }
58
59         @Override
60         public void visitErrorNode(final ErrorNode node) {
61             // No-op
62         }
63     };
64
65     private final YangStatementParserListenerImpl yangStatementModelParser;
66     private final SourceIdentifier identifier;
67     private final StatementContext context;
68
69     private YangStatementStreamSource(final SourceIdentifier identifier, final YangStatementParserListenerImpl parser,
70             final StatementContext context) {
71         this.identifier = Preconditions.checkNotNull(identifier);
72         this.yangStatementModelParser = Preconditions.checkNotNull(parser);
73         this.context = Preconditions.checkNotNull(context);
74     }
75
76     public static YangStatementStreamSource create(final YangTextSchemaSource source) throws IOException,
77             YangSyntaxErrorException {
78         final StatementContext context;
79         try (InputStream stream = source.openStream()) {
80             context = parseYangSource(source.getIdentifier(), stream);
81         }
82
83         final String sourceName = source.getSymbolicName().orElse(null);
84         final YangStatementParserListenerImpl parser = new YangStatementParserListenerImpl(sourceName);
85         return new YangStatementStreamSource(source.getIdentifier(), parser, context);
86     }
87
88     public static YangStatementStreamSource create(final SourceIdentifier identifier, final StatementContext context,
89         final String symbolicName) {
90         return new YangStatementStreamSource(identifier, new YangStatementParserListenerImpl(symbolicName), context);
91     }
92
93     @Override
94     public void writePreLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
95         yangStatementModelParser.setAttributes(writer, stmtDef);
96         ParseTreeWalker.DEFAULT.walk(yangStatementModelParser, context);
97     }
98
99     @Override
100     public void writeLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
101             final PrefixToModule preLinkagePrefixes) {
102         writeLinkage(writer, stmtDef, preLinkagePrefixes, YangVersion.VERSION_1);
103     }
104
105     @Override
106     public void writeLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
107             final PrefixToModule preLinkagePrefixes, final YangVersion yangVersion) {
108         yangStatementModelParser.setAttributes(writer, stmtDef, preLinkagePrefixes, yangVersion);
109         ParseTreeWalker.DEFAULT.walk(yangStatementModelParser, context);
110     }
111
112     @Override
113     public void writeLinkageAndStatementDefinitions(final StatementWriter writer,
114             final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes) {
115         writeLinkageAndStatementDefinitions(writer, stmtDef, prefixes, YangVersion.VERSION_1);
116     }
117
118     @Override
119     public void writeLinkageAndStatementDefinitions(final StatementWriter writer,
120             final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes, final YangVersion yangVersion) {
121         yangStatementModelParser.setAttributes(writer, stmtDef, prefixes, yangVersion);
122         ParseTreeWalker.DEFAULT.walk(yangStatementModelParser, context);
123     }
124
125     @Override
126     public void writeFull(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
127             final PrefixToModule prefixes) {
128         writeFull(writer, stmtDef, prefixes, YangVersion.VERSION_1);
129     }
130
131     @Override
132     public void writeFull(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
133             final PrefixToModule prefixes, final YangVersion yangVersion) {
134         yangStatementModelParser.setAttributes(writer, stmtDef, prefixes, yangVersion);
135         ParseTreeWalker.DEFAULT.walk(yangStatementModelParser, context);
136     }
137
138     @Override
139     public SourceIdentifier getIdentifier() {
140         return identifier;
141     }
142
143     public ParserRuleContext getYangAST() {
144         return context;
145     }
146
147     private static StatementContext parseYangSource(final SourceIdentifier source, final InputStream stream)
148             throws IOException, YangSyntaxErrorException {
149         final YangStatementLexer lexer = new YangStatementLexer(CharStreams.fromStream(stream));
150         final CommonTokenStream tokens = new CommonTokenStream(lexer);
151         final YangStatementParser parser = new YangStatementParser(tokens);
152         //disconnect from console error output
153         parser.removeErrorListeners();
154
155         final YangErrorListener errorListener = new YangErrorListener(source);
156         parser.addErrorListener(errorListener);
157
158         final StatementContext result = parser.statement();
159         errorListener.validate();
160
161         // Walk the resulting tree and replace each children with an immutable list, lowering memory requirements
162         // and making sure the resulting tree will not get accidentally modified. An alternative would be to use
163         // org.antlr.v4.runtime.Parser.TrimToSizeListener, but that does not make the tree immutable.
164         ParseTreeWalker.DEFAULT.walk(MAKE_IMMUTABLE_LISTENER, result);
165
166         return result;
167     }
168
169     @Override
170     public String toString() {
171         return MoreObjects.toStringHelper(this).add("identifier", getIdentifier()).toString();
172     }
173 }