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