Turn YangStatementParserListenerImpl into a visitor
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / repo / StatementContextVisitor.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.repo;
9
10 import static com.google.common.base.Verify.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
12
13 import org.antlr.v4.runtime.tree.ParseTree;
14 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.ArgumentContext;
15 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.KeywordContext;
16 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
17 import org.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.common.QNameModule;
19 import org.opendaylight.yangtools.yang.common.YangConstants;
20 import org.opendaylight.yangtools.yang.common.YangVersion;
21 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
22 import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource;
23 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
24 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
25 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
26 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
27 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
28
29 abstract class StatementContextVisitor {
30     static final class Loose extends StatementContextVisitor {
31         Loose(final String sourceName, final StatementWriter writer,
32             final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes, final YangVersion yangVersion) {
33             super(sourceName, writer, stmtDef, prefixes, yangVersion);
34         }
35
36         Loose(final String sourceName, final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
37             this(sourceName, writer, stmtDef, null, YangVersion.VERSION_1);
38         }
39     }
40
41     static final class Strict extends StatementContextVisitor {
42         Strict(final String sourceName, final StatementWriter writer,
43             final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes, final YangVersion yangVersion) {
44             super(sourceName, writer, stmtDef, prefixes, yangVersion);
45         }
46
47         @Override
48         QName getValidStatementDefinition(final String keywordText, final StatementSourceReference ref) {
49             return SourceException.throwIfNull(super.getValidStatementDefinition(keywordText, ref), ref,
50                 "%s is not a YANG statement or use of extension.", keywordText);
51         }
52     }
53
54     private final QNameToStatementDefinition stmtDef;
55     private final StatementWriter writer;
56     private final YangVersion yangVersion;
57     private final PrefixToModule prefixes;
58     private final String sourceName;
59
60     private StatementContextVisitor(final String sourceName, final StatementWriter writer,
61             final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes, final YangVersion yangVersion) {
62         this.writer = requireNonNull(writer);
63         this.stmtDef = requireNonNull(stmtDef);
64         this.yangVersion = requireNonNull(yangVersion);
65         this.sourceName = sourceName;
66         this.prefixes = prefixes;
67     }
68
69     void visit(final StatementContext context) {
70         processStatement(0, context);
71     }
72
73     /**
74      * Based on identifier read from source and collections of relevant prefixes and statement definitions mappings
75      * provided for actual phase, method resolves and returns valid QName for declared statement to be written.
76      * This applies to any declared statement, including unknown statements.
77      *
78      * @param prefixes collection of all relevant prefix mappings supplied for actual parsing phase
79      * @param stmtDef collection of all relevant statement definition mappings provided for actual parsing phase
80      * @param keywordText statement keyword text to parse from source
81      * @param ref Source reference
82      * @return valid QName for declared statement to be written, or null
83      */
84     QName getValidStatementDefinition(final String keywordText, final StatementSourceReference ref) {
85         final int firstColon = keywordText.indexOf(':');
86         if (firstColon == -1) {
87             final StatementDefinition def = stmtDef.get(QName.create(YangConstants.RFC6020_YIN_NAMESPACE, keywordText));
88             return def != null ? def.getStatementName() : null;
89         }
90
91         SourceException.throwIf(firstColon == keywordText.length() - 1
92                 || keywordText.indexOf(':', firstColon + 1) != -1, ref, "Malformed statement '%s'", keywordText);
93
94         if (prefixes == null) {
95             // No prefixes to look up from
96             return null;
97         }
98
99         final String prefix = keywordText.substring(0, firstColon);
100         final QNameModule qNameModule = prefixes.get(prefix);
101         if (qNameModule == null) {
102             // Failed to look the namespace
103             return null;
104         }
105
106         final String localName = keywordText.substring(firstColon + 1);
107         final StatementDefinition foundStmtDef;
108         if (prefixes.isPreLinkageMap()) {
109             foundStmtDef = stmtDef.getByNamespaceAndLocalName(qNameModule.getNamespace(), localName);
110         } else {
111             foundStmtDef = stmtDef.get(QName.create(qNameModule, localName));
112         }
113
114         return foundStmtDef != null ? foundStmtDef.getStatementName() : null;
115     }
116
117     private void processStatement(final int myOffset, final StatementContext ctx) {
118         final String keywordTxt = verifyNotNull(ctx.getChild(KeywordContext.class, 0)).getText();
119         final StatementSourceReference ref = DeclarationInTextSource.atPosition(sourceName, ctx.getStart().getLine(),
120             ctx.getStart().getCharPositionInLine());
121         final QName def = getValidStatementDefinition(keywordTxt, ref);
122         if (def == null) {
123             return;
124         }
125
126         final ArgumentContext argumentCtx = ctx.getChild(ArgumentContext.class, 0);
127         final String argument = argumentCtx == null ? null
128                 : ArgumentContextUtils.stringFromStringContext(argumentCtx, yangVersion, ref);
129         writer.startStatement(myOffset, def, argument, ref);
130
131         if (ctx.children != null) {
132             int childOffset = 0;
133             for (ParseTree s : ctx.children) {
134                 if (s instanceof StatementContext) {
135                     processStatement(childOffset++, (StatementContext) s);
136                 }
137             }
138         }
139
140         writer.endStatement(ref);
141     }
142 }