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