Merge branch 'master' of ../controller
[yangtools.git] / yang / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / SubstatementContext.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 java.util.Objects.requireNonNull;
11
12 import com.google.common.base.Verify;
13 import java.util.Optional;
14 import org.opendaylight.yangtools.util.OptionalBoolean;
15 import org.opendaylight.yangtools.yang.common.QName;
16 import org.opendaylight.yangtools.yang.common.QNameModule;
17 import org.opendaylight.yangtools.yang.common.YangVersion;
18 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
19 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
20 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
21 import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
22 import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatement;
23 import org.opendaylight.yangtools.yang.model.api.stmt.DeviationStatement;
24 import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement;
25 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
26 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
27 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
28 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
29 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
30 import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
36 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
37
38 final class SubstatementContext<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>> extends
39         StatementContextBase<A, D, E> {
40     private final StatementContextBase<?, ?, ?> parent;
41     private final A argument;
42
43     /**
44      * Config statements are not all that common which means we are performing a recursive search towards the root
45      * every time {@link #isConfiguration()} is invoked. This is quite expensive because it causes a linear search
46      * for the (usually non-existent) config statement.
47      *
48      * <p>
49      * This field maintains a resolution cache, so once we have returned a result, we will keep on returning the same
50      * result without performing any lookups.
51      */
52     // BooleanField value
53     private byte configuration;
54
55     /**
56      * This field maintains a resolution cache for ignore config, so once we have returned a result, we will
57      * keep on returning the same result without performing any lookups.
58      */
59     // BooleanField value
60     private byte ignoreConfig;
61
62     /**
63      * This field maintains a resolution cache for ignore if-feature, so once we have returned a result, we will
64      * keep on returning the same result without performing any lookups.
65      */
66     // BooleanField value
67     private byte ignoreIfFeature;
68
69     private volatile SchemaPath schemaPath;
70
71     SubstatementContext(final StatementContextBase<?, ?, ?> parent, final StatementDefinitionContext<A, D, E> def,
72             final StatementSourceReference ref, final String rawArgument) {
73         super(def, ref, rawArgument);
74         this.parent = requireNonNull(parent, "Parent must not be null");
75         this.argument = def.parseArgumentValue(this, rawStatementArgument());
76     }
77
78     SubstatementContext(final StatementContextBase<?, ?, ?> parent, final StatementDefinitionContext<A, D, E> def,
79         final StatementSourceReference ref, final String rawArgument, final A argument, final CopyType copyType) {
80         super(def, ref, rawArgument, copyType);
81         this.parent = requireNonNull(parent, "Parent must not be null");
82         this.argument = argument;
83     }
84
85     SubstatementContext(final StatementContextBase<A, D, E> original, final StatementContextBase<?, ?, ?> parent,
86             final CopyType copyType, final QNameModule targetModule) {
87         super(original, copyType);
88         this.parent = requireNonNull(parent, "Parent must not be null");
89         this.argument = targetModule == null ? original.getStatementArgument()
90                 : original.definition().adaptArgumentValue(original, targetModule);
91     }
92
93     SubstatementContext(final StatementContextBase<A, D, E> original, final StatementContextBase<?, ?, ?> parent) {
94         super(original);
95         this.parent = requireNonNull(parent, "Parent must not be null");
96         this.argument = original.getStatementArgument();
97     }
98
99     @Override
100     public StatementContextBase<?, ?, ?> getParentContext() {
101         return parent;
102     }
103
104     @Override
105     public StorageNodeType getStorageNodeType() {
106         return StorageNodeType.STATEMENT_LOCAL;
107     }
108
109     @Override
110     public NamespaceStorageNode getParentNamespaceStorage() {
111         return parent;
112     }
113
114     @Override
115     public Registry getBehaviourRegistry() {
116         return parent.getBehaviourRegistry();
117     }
118
119     @Override
120     public RootStatementContext<?, ?, ?> getRoot() {
121         return parent.getRoot();
122     }
123
124     @Override
125     public A getStatementArgument() {
126         return argument;
127     }
128
129     private SchemaPath createSchemaPath() {
130         final Optional<SchemaPath> maybeParentPath = parent.getSchemaPath();
131         Verify.verify(maybeParentPath.isPresent(), "Parent %s does not have a SchemaPath", parent);
132         final SchemaPath parentPath = maybeParentPath.get();
133
134         if (StmtContextUtils.isUnknownStatement(this)) {
135             return parentPath.createChild(getPublicDefinition().getStatementName());
136         }
137         if (argument instanceof QName) {
138             final QName qname = (QName) argument;
139             if (StmtContextUtils.producesDeclared(this, UsesStatement.class)) {
140                 return maybeParentPath.orElse(null);
141             }
142
143             return parentPath.createChild(qname);
144         }
145         if (argument instanceof String) {
146             // FIXME: This may yield illegal argument exceptions
147             final Optional<StmtContext<?, ?, ?>> originalCtx = getOriginalCtx();
148             final QName qname = StmtContextUtils.qnameFromArgument(originalCtx.orElse(this), (String) argument);
149             return parentPath.createChild(qname);
150         }
151         if (argument instanceof SchemaNodeIdentifier
152                 && (StmtContextUtils.producesDeclared(this, AugmentStatement.class)
153                         || StmtContextUtils.producesDeclared(this, RefineStatement.class)
154                         || StmtContextUtils.producesDeclared(this, DeviationStatement.class))) {
155
156             return parentPath.createChild(((SchemaNodeIdentifier) argument).getPathFromRoot());
157         }
158
159         // FIXME: this does not look right
160         return maybeParentPath.orElse(null);
161     }
162
163     @Override
164     public Optional<SchemaPath> getSchemaPath() {
165         SchemaPath local = schemaPath;
166         if (local == null) {
167             synchronized (this) {
168                 local = schemaPath;
169                 if (local == null) {
170                     local = createSchemaPath();
171                     schemaPath = local;
172                 }
173             }
174         }
175
176         return Optional.ofNullable(local);
177     }
178
179     @Override
180     public boolean isConfiguration() {
181         if (isIgnoringConfig()) {
182             return true;
183         }
184
185         if (OptionalBoolean.isPresent(configuration)) {
186             return OptionalBoolean.get(configuration);
187         }
188
189         final StmtContext<Boolean, ?, ?> configStatement = StmtContextUtils.findFirstSubstatement(this,
190             ConfigStatement.class);
191         final boolean parentIsConfig = parent.isConfiguration();
192
193         final boolean isConfig;
194         if (configStatement != null) {
195             isConfig = configStatement.coerceStatementArgument();
196
197             // Validity check: if parent is config=false this cannot be a config=true
198             InferenceException.throwIf(isConfig && !parentIsConfig, getStatementSourceReference(),
199                     "Parent node has config=false, this node must not be specifed as config=true");
200         } else {
201             // If "config" statement is not specified, the default is the same as the parent's "config" value.
202             isConfig = parentIsConfig;
203         }
204
205         // Resolved, make sure we cache this return
206         configuration = OptionalBoolean.of(isConfig);
207         return isConfig;
208     }
209
210     @Override
211     public boolean isEnabledSemanticVersioning() {
212         return parent.isEnabledSemanticVersioning();
213     }
214
215     @Override
216     public YangVersion getRootVersion() {
217         return getRoot().getRootVersion();
218     }
219
220     @Override
221     public void setRootVersion(final YangVersion version) {
222         getRoot().setRootVersion(version);
223     }
224
225     @Override
226     public void addMutableStmtToSeal(final MutableStatement mutableStatement) {
227         getRoot().addMutableStmtToSeal(mutableStatement);
228     }
229
230     @Override
231     public void addRequiredSource(final SourceIdentifier dependency) {
232         getRoot().addRequiredSource(dependency);
233     }
234
235     @Override
236     public void setRootIdentifier(final SourceIdentifier identifier) {
237         getRoot().setRootIdentifier(identifier);
238     }
239
240     @Override
241     protected boolean isIgnoringIfFeatures() {
242         if (OptionalBoolean.isPresent(ignoreIfFeature)) {
243             return OptionalBoolean.get(ignoreIfFeature);
244         }
245
246         final boolean ret = definition().isIgnoringIfFeatures() || parent.isIgnoringIfFeatures();
247         ignoreIfFeature = OptionalBoolean.of(ret);
248
249         return ret;
250     }
251
252     @Override
253     protected boolean isIgnoringConfig() {
254         if (OptionalBoolean.isPresent(ignoreConfig)) {
255             return OptionalBoolean.get(ignoreConfig);
256         }
257
258         final boolean ret = definition().isIgnoringConfig() || parent.isIgnoringConfig();
259         ignoreConfig = OptionalBoolean.of(ret);
260
261         return ret;
262     }
263
264     @Override
265     protected boolean isParentSupportedByFeatures() {
266         return parent.isSupportedByFeatures();
267     }
268 }