Populate xpath/ hierarchy
[yangtools.git] / parser / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / StatementContextWriter.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. 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.stmt.reactor;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12 import static com.google.common.base.Verify.verify;
13 import static com.google.common.base.Verify.verifyNotNull;
14 import static java.util.Objects.requireNonNull;
15
16 import java.util.Optional;
17 import org.eclipse.jdt.annotation.Nullable;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.model.api.meta.StatementOrigin;
20 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
21 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
22 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
23
24 final class StatementContextWriter implements StatementWriter {
25     private final ModelProcessingPhase phase;
26     private final SourceSpecificContext ctx;
27
28     private AbstractResumedStatement<?, ?, ?> current;
29
30     StatementContextWriter(final SourceSpecificContext ctx, final ModelProcessingPhase phase) {
31         this.ctx = requireNonNull(ctx);
32         this.phase = requireNonNull(phase);
33     }
34
35     @Override
36     public Optional<? extends ResumedStatement> resumeStatement(final int childId) {
37         final AbstractResumedStatement<?, ?, ?> existing = lookupDeclaredChild(current, childId);
38         if (existing != null) {
39             resumeStatement(existing);
40             return Optional.of(existing);
41         }
42         return Optional.empty();
43     }
44
45     private void resumeStatement(final AbstractResumedStatement<?, ?, ?> child) {
46         if (child.isFullyDefined()) {
47             child.walkChildren(phase);
48             child.endDeclared(phase);
49         } else {
50             current = child;
51         }
52     }
53
54     @Override
55     public void storeStatement(final int expectedChildren, final boolean fullyDefined) {
56         checkState(current != null);
57         checkArgument(expectedChildren >= 0);
58         current.resizeSubstatements(expectedChildren);
59
60         if (fullyDefined) {
61             current.setFullyDefined();
62         }
63     }
64
65     @Override
66     public void startStatement(final int childId, final QName name, final String argument,
67             final StatementSourceReference ref) {
68         final AbstractResumedStatement<?, ?, ?> existing = lookupDeclaredChild(current, childId);
69         current = existing != null ? existing
70                 : verifyNotNull(ctx.createDeclaredChild(current, childId, name, argument, ref));
71     }
72
73     @Override
74     public void endStatement(final StatementSourceReference ref) {
75         checkState(current != null);
76         current.endDeclared(phase);
77         exitStatement();
78     }
79
80     @Override
81     public ModelProcessingPhase getPhase() {
82         return phase;
83     }
84
85     private void exitStatement() {
86         // TODO: AbstractResumedStatement should only ever have AbstractResumedStatement parents, which would:
87         //       - remove the StatementSource check
88         //       - allow endDeclared() to be moved to AbstractResumedStatement
89         //       - remove the need for verify()
90         StatementContextBase<?, ?, ?> parentContext = current.getParentContext();
91         while (parentContext != null && StatementOrigin.CONTEXT == parentContext.origin()) {
92             parentContext.endDeclared(phase);
93             parentContext = parentContext.getParentContext();
94         }
95         if (parentContext != null) {
96             verify(parentContext instanceof AbstractResumedStatement, "Unexpected parent context %s", parentContext);
97             current = (AbstractResumedStatement<?, ?, ?>) parentContext;
98         } else {
99             current = null;
100         }
101     }
102
103     private static @Nullable AbstractResumedStatement<?, ?, ?> lookupDeclaredChild(
104             final AbstractResumedStatement<?, ?, ?> current, final int childId) {
105         if (current == null) {
106             return null;
107         }
108
109         // Fast path: we are entering a statement which was emitted in previous phase
110         AbstractResumedStatement<?, ?, ?> existing = current.lookupSubstatement(childId);
111         while (existing != null && StatementOrigin.CONTEXT == existing.origin()) {
112             existing = existing.lookupSubstatement(childId);
113         }
114
115         return existing;
116     }
117 }