Bug 4933: Yang parser does not accept deviate "not-supported"
[yangtools.git] / yang / yang-parser-impl / 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 com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Verify;
13 import java.util.Collection;
14 import org.opendaylight.yangtools.yang.common.QName;
15 import org.opendaylight.yangtools.yang.common.QNameModule;
16 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
17 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
18 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
19 import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
20 import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceStatement;
21 import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatement;
22 import org.opendaylight.yangtools.yang.model.api.stmt.KeyStatement;
23 import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement;
24 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
25 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
26 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
27 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
28 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry;
29 import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
30 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
32 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
33 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
34 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
35 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.GroupingUtils;
36 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
37
38 final class SubstatementContext<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
39         extends StatementContextBase<A, D, E> {
40
41     private final StatementContextBase<?, ?, ?> parent;
42     private final A argument;
43     private volatile SchemaPath schemaPath;
44
45     SubstatementContext(final StatementContextBase<?, ?, ?> parent,
46             final ContextBuilder<A, D, E> builder) throws SourceException {
47         super(builder);
48         this.parent = Preconditions.checkNotNull(parent, "Parent must not be null");
49         this.argument = builder.getDefinition().parseArgumentValue(this, builder.getRawArgument());
50     }
51
52     @SuppressWarnings("unchecked")
53     SubstatementContext(final SubstatementContext<A, D, E> original,
54             final QNameModule newQNameModule,
55             final StatementContextBase<?, ?, ?> newParent, final TypeOfCopy typeOfCopy) {
56         super(original);
57         this.parent = newParent;
58
59         if (newQNameModule != null) {
60             if (original.argument instanceof QName) {
61                 QName originalQName = (QName) original.argument;
62                 this.argument = (A)
63                         getFromNamespace(QNameCacheNamespace.class,
64                             QName.create(newQNameModule, originalQName.getLocalName()));
65             } else if (StmtContextUtils.producesDeclared(original, KeyStatement.class)) {
66                 this.argument = (A) StmtContextUtils.replaceModuleQNameForKey(
67                                 (StmtContext<Collection<SchemaNodeIdentifier>, KeyStatement, ?>) original,
68                                 newQNameModule);
69             } else {
70                 this.argument = original.argument;
71             }
72         } else {
73             this.argument = original.argument;
74         }
75     }
76
77
78     private void copyDeclaredStmts(final SubstatementContext<A, D, E> original,
79             final QNameModule newQNameModule, final TypeOfCopy typeOfCopy)
80             throws SourceException {
81         Collection<? extends StatementContextBase<?, ?, ?>> originalDeclaredSubstatements = original
82                 .declaredSubstatements();
83         for (StatementContextBase<?, ?, ?> stmtContext : originalDeclaredSubstatements) {
84             if (GroupingUtils.needToCopyByUses(stmtContext)) {
85                 StatementContextBase<?, ?, ?> copy = stmtContext.createCopy(
86                         newQNameModule, this, typeOfCopy);
87                 this.addEffectiveSubstatement(copy);
88             } else if (GroupingUtils.isReusedByUses(stmtContext)) {
89                 this.addEffectiveSubstatement(stmtContext);
90             }
91         }
92     }
93
94     private void copyEffectiveStmts(final SubstatementContext<A, D, E> original,
95             final QNameModule newQNameModule, final TypeOfCopy typeOfCopy)
96             throws SourceException {
97         Collection<? extends StatementContextBase<?, ?, ?>> originalEffectiveSubstatements = original
98                 .effectiveSubstatements();
99         for (StatementContextBase<?, ?, ?> stmtContext : originalEffectiveSubstatements) {
100             if (GroupingUtils.needToCopyByUses(stmtContext)) {
101                 StatementContextBase<?, ?, ?> copy = stmtContext.createCopy(
102                         newQNameModule, this, typeOfCopy);
103                 this.addEffectiveSubstatement(copy);
104             } else if (GroupingUtils.isReusedByUses(stmtContext)) {
105                 this.addEffectiveSubstatement(stmtContext);
106             }
107         }
108     }
109
110     @Override
111     public StatementContextBase<?, ?, ?> getParentContext() {
112         return parent;
113     }
114
115     @Override
116     public NamespaceStorageNode getParentNamespaceStorage() {
117         return parent;
118     }
119
120     @Override
121     public Registry getBehaviourRegistry() {
122         return parent.getBehaviourRegistry();
123     }
124
125     @Override
126     public RootStatementContext<?, ?, ?> getRoot() {
127         return parent.getRoot();
128     }
129
130     @Override
131     public A getStatementArgument() {
132         return argument;
133     }
134
135     @Override
136     public StatementContextBase<?, ?, ?> createCopy(
137             final StatementContextBase<?, ?, ?> newParent, final TypeOfCopy typeOfCopy)
138             throws SourceException {
139         return createCopy(null, newParent, typeOfCopy);
140     }
141
142     @Override
143     public StatementContextBase<A, D, E> createCopy(final QNameModule newQNameModule,
144             final StatementContextBase<?, ?, ?> newParent, final TypeOfCopy typeOfCopy)
145             throws SourceException {
146         SubstatementContext<A, D, E> copy = new SubstatementContext<>(this, newQNameModule, newParent, typeOfCopy);
147
148         copy.addAllToCopyHistory(this.getCopyHistory());
149         copy.addToCopyHistory(typeOfCopy);
150
151         if(this.getOriginalCtx() != null) {
152             copy.setOriginalCtx(this.getOriginalCtx());
153         } else {
154             copy.setOriginalCtx(this);
155         }
156
157         definition().onStatementAdded(copy);
158
159         copy.copyDeclaredStmts(this, newQNameModule, typeOfCopy);
160         copy.copyEffectiveStmts(this, newQNameModule, typeOfCopy);
161         return copy;
162     }
163
164     private boolean isSupportedAsShorthandCase() {
165         final Collection<?> supportedCaseShorthands = getFromNamespace(ValidationBundlesNamespace.class,
166                 ValidationBundleType.SUPPORTED_CASE_SHORTHANDS);
167         return supportedCaseShorthands == null || supportedCaseShorthands.contains(getPublicDefinition());
168     }
169
170     private SchemaPath createSchemaPath() {
171         final Optional<SchemaPath> maybeParentPath = parent.getSchemaPath();
172         Verify.verify(maybeParentPath.isPresent(), "Parent %s does not have a SchemaPath", parent);
173         final SchemaPath parentPath = maybeParentPath.get();
174
175         if (argument instanceof QName) {
176             QName qname = (QName) argument;
177             if (StmtContextUtils.producesDeclared(this, UsesStatement.class)) {
178                 return maybeParentPath.orNull();
179             }
180
181             final SchemaPath path;
182             if (StmtContextUtils.producesDeclared(getParentContext(), ChoiceStatement.class)
183                     && isSupportedAsShorthandCase()) {
184                 path = parentPath.createChild(qname);
185             } else {
186                 path = parentPath;
187             }
188             return path.createChild(qname);
189         }
190         if (argument instanceof String) {
191             // FIXME: This may yield illegal argument exceptions
192             final StatementContextBase<?, ?, ?> originalCtx = getOriginalCtx();
193             final QName qname = (originalCtx != null) ? Utils.qNameFromArgument(originalCtx, (String) argument)
194                     : Utils.qNameFromArgument(this, (String) argument);
195             return parentPath.createChild(qname);
196         }
197         if (argument instanceof SchemaNodeIdentifier &&
198                 (StmtContextUtils.producesDeclared(this, AugmentStatement.class)
199                    || StmtContextUtils.producesDeclared(this, RefineStatement.class))) {
200
201             return parentPath.createChild(((SchemaNodeIdentifier) argument).getPathFromRoot());
202         }
203         if (Utils.isUnknownNode(this)) {
204             return parentPath.createChild(getPublicDefinition().getStatementName());
205         }
206
207         // FIXME: this does not look right
208         return maybeParentPath.orNull();
209     }
210
211     @Override
212     public Optional<SchemaPath> getSchemaPath() {
213         SchemaPath local = schemaPath;
214         if (local == null) {
215             synchronized (this) {
216                 local = schemaPath;
217                 if (local == null) {
218                     local = createSchemaPath();
219                     schemaPath = local;
220                 }
221             }
222
223         }
224
225         return Optional.fromNullable(local);
226     }
227
228     @Override
229     public boolean isRootContext() {
230         return false;
231     }
232
233     @Override
234     public boolean isConfiguration() {
235         StmtContext<Boolean, ?, ?> configStatement = StmtContextUtils
236                 .findFirstSubstatement(this, ConfigStatement.class);
237
238         /*
239          * If "config" statement is not specified, the default is the same as
240          * the parent schema node's "config" value.
241          */
242         if (configStatement == null) {
243             return parent.isConfiguration();
244         }
245
246         /*
247          * If a parent node has "config" set to "true", the node underneath it can
248          * have "config" set to "true" or "false".
249          */
250         if (parent.isConfiguration()) {
251             return configStatement.getStatementArgument();
252         }
253
254         /*
255          * If a parent node has "config" set to "false", no node underneath it
256          * can have "config" set to "true", therefore only "false" is permitted.
257          */
258         if (!configStatement.getStatementArgument()) {
259             return false;
260         }
261
262         throw new InferenceException(
263                 "Parent node has config statement set to false, therefore no node underneath it can have config set to true",
264                 getStatementSourceReference());
265     }
266 }