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