Add an explicit intermediate YANG representation
[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 java.util.Objects.requireNonNull;
11
12 import java.util.Optional;
13 import org.opendaylight.yangtools.yang.common.QName;
14 import org.opendaylight.yangtools.yang.common.QNameModule;
15 import org.opendaylight.yangtools.yang.common.YangConstants;
16 import org.opendaylight.yangtools.yang.common.YangVersion;
17 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
18 import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRArgument;
19 import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRKeyword;
20 import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRKeyword.Qualified;
21 import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRStatement;
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.StatementSourceReference;
26 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
27 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter.ResumedStatement;
28
29 class StatementContextVisitor {
30     private final QNameToStatementDefinition stmtDef;
31     private final ArgumentContextUtils utils;
32     private final StatementWriter writer;
33     private final PrefixToModule prefixes;
34     private final String sourceName;
35
36     StatementContextVisitor(final String sourceName, final StatementWriter writer,
37             final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes, final YangVersion yangVersion) {
38         this.writer = requireNonNull(writer);
39         this.stmtDef = requireNonNull(stmtDef);
40         this.utils = ArgumentContextUtils.forVersion(yangVersion);
41         this.sourceName = sourceName;
42         this.prefixes = prefixes;
43     }
44
45     void visit(final IRStatement stmt) {
46         processStatement(0, stmt);
47     }
48
49     /**
50      * Based on identifier read from source and collections of relevant prefixes and statement definitions mappings
51      * provided for actual phase, method resolves and returns valid QName for declared statement to be written.
52      * This applies to any declared statement, including unknown statements.
53      *
54      * @param prefixes collection of all relevant prefix mappings supplied for actual parsing phase
55      * @param stmtDef collection of all relevant statement definition mappings provided for actual parsing phase
56      * @param keyword statement keyword text to parse from source
57      * @param ref Source reference
58      * @return valid QName for declared statement to be written, or null
59      */
60     QName getValidStatementDefinition(final IRKeyword keyword, final StatementSourceReference ref) {
61         if (keyword instanceof Qualified) {
62             return getValidStatementDefinition((Qualified) keyword, ref);
63         }
64         final StatementDefinition def = stmtDef.get(QName.create(YangConstants.RFC6020_YIN_MODULE,
65             keyword.identifier()));
66         return def != null ? def.getStatementName() : null;
67     }
68
69     private QName getValidStatementDefinition(final Qualified keyword, final StatementSourceReference ref) {
70         if (prefixes == null) {
71             // No prefixes to look up from
72             return null;
73         }
74
75         final QNameModule qNameModule = prefixes.get(keyword.prefix());
76         if (qNameModule == null) {
77             // Failed to look the namespace
78             return null;
79         }
80
81         final StatementDefinition foundStmtDef = resolveStatement(qNameModule, keyword.identifier());
82         return foundStmtDef != null ? foundStmtDef.getStatementName() : null;
83     }
84
85     StatementDefinition resolveStatement(final QNameModule module, final String localName) {
86         return stmtDef.get(QName.create(module, localName));
87     }
88
89     // Normal entry point, checks for potential resume
90     private boolean processStatement(final int myOffset, final IRStatement stmt) {
91         final Optional<? extends ResumedStatement> optResumed = writer.resumeStatement(myOffset);
92         if (optResumed.isPresent()) {
93             final ResumedStatement resumed = optResumed.get();
94             return resumed.isFullyDefined() || doProcessStatement(stmt, resumed.getSourceReference());
95         }
96         return processNewStatement(myOffset, stmt);
97     }
98
99     // Slow-path allocation of a new statement
100     private boolean processNewStatement(final int myOffset, final IRStatement stmt) {
101         final StatementSourceReference ref = DeclarationInTextSource.atPosition(sourceName, stmt.startLine(),
102             stmt.startColumn());
103         final QName def = getValidStatementDefinition(stmt.keyword(), ref);
104         if (def == null) {
105             return false;
106         }
107
108         final IRArgument argumentCtx = stmt.argument();
109         final String argument = argumentCtx == null ? null : utils.stringFromStringContext(argumentCtx, ref);
110         writer.startStatement(myOffset, def, argument, ref);
111         return doProcessStatement(stmt, ref);
112     }
113
114     // Actual processing
115     private boolean doProcessStatement(final IRStatement stmt, final StatementSourceReference ref) {
116         int childOffset = 0;
117         boolean fullyDefined = true;
118         for (IRStatement substatement : stmt.statements()) {
119             if (!processStatement(childOffset++, substatement)) {
120                 fullyDefined = false;
121             }
122         }
123
124         writer.storeStatement(childOffset, fullyDefined);
125         writer.endStatement(ref);
126         return fullyDefined;
127     }
128 }