Bug 6261: Introduce resolution of deviation statement during SchemaContext assembly
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / spi / meta / StmtContextUtils.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.spi.meta;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.base.Splitter;
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.collect.ImmutableSet.Builder;
15 import java.util.Collection;
16 import java.util.Set;
17 import java.util.function.Predicate;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.common.QNameModule;
20 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
21 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
22 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
23 import org.opendaylight.yangtools.yang.model.api.stmt.KeyStatement;
24 import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
25 import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsStatement;
26 import org.opendaylight.yangtools.yang.model.api.stmt.PresenceStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
28 import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace;
29 import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace.SupportedFeatures;
30 import org.opendaylight.yangtools.yang.parser.stmt.reactor.RootStatementContext;
31 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
32 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.UnknownStatementImpl;
33
34 public final class StmtContextUtils {
35     public static final Splitter LIST_KEY_SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults();
36
37     private StmtContextUtils() {
38         throw new UnsupportedOperationException("Utility class");
39     }
40
41     @SuppressWarnings("unchecked")
42     public static <AT, DT extends DeclaredStatement<AT>> AT firstAttributeOf(
43             final Iterable<? extends StmtContext<?, ?, ?>> contexts, final Class<DT> declaredType) {
44         for (final StmtContext<?, ?, ?> ctx : contexts) {
45             if (producesDeclared(ctx, declaredType)) {
46                 return (AT) ctx.getStatementArgument();
47             }
48         }
49         return null;
50     }
51
52     @SuppressWarnings("unchecked")
53     public static <AT, DT extends DeclaredStatement<AT>> AT firstAttributeOf(final StmtContext<?, ?, ?> ctx,
54             final Class<DT> declaredType) {
55         return producesDeclared(ctx, declaredType) ? (AT) ctx.getStatementArgument() : null;
56     }
57
58     public static <AT, DT extends DeclaredStatement<AT>> AT firstSubstatementAttributeOf(
59             final StmtContext<?, ?, ?> ctx, final Class<DT> declaredType) {
60         final AT firstAttribute = firstAttributeOf(ctx.effectiveSubstatements(), declaredType);
61         return firstAttribute != null ? firstAttribute : firstAttributeOf(ctx.declaredSubstatements(), declaredType);
62     }
63
64     @SuppressWarnings("unchecked")
65     public static <AT, DT extends DeclaredStatement<AT>> StmtContext<AT, ?, ?> findFirstDeclaredSubstatement(
66             final StmtContext<?, ?, ?> stmtContext, final Class<DT> declaredType) {
67         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
68             if (producesDeclared(subStmtContext, declaredType)) {
69                 return (StmtContext<AT, ?, ?>) subStmtContext;
70             }
71         }
72         return null;
73     }
74
75     @SuppressWarnings("unchecked")
76     public static <AT, DT extends DeclaredStatement<AT>> Collection<StmtContext<AT, DT, ?>> findAllDeclaredSubstatements(
77             final StmtContext<?, ?, ?> stmtContext, final Class<DT> declaredType) {
78         final ImmutableList.Builder<StmtContext<AT, DT, ?>> listBuilder = ImmutableList.builder();
79         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
80             if (producesDeclared(subStmtContext, declaredType)) {
81                 listBuilder.add((StmtContext<AT, DT, ?>) subStmtContext);
82             }
83         }
84         return listBuilder.build();
85     }
86
87     @SuppressWarnings("unchecked")
88     public static <AT, DT extends DeclaredStatement<AT>> Collection<StmtContext<AT, DT, ?>> findAllEffectiveSubstatements(
89             final StmtContext<?, ?, ?> stmtContext, final Class<DT> type) {
90         final ImmutableList.Builder<StmtContext<AT, DT, ?>> listBuilder = ImmutableList.builder();
91         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.effectiveSubstatements()) {
92             if (producesDeclared(subStmtContext, type)) {
93                 listBuilder.add((StmtContext<AT, DT, ?>) subStmtContext);
94             }
95         }
96         return listBuilder.build();
97     }
98
99     public static <AT, DT extends DeclaredStatement<AT>> Collection<StmtContext<AT, DT, ?>> findAllSubstatements(
100             final StmtContext<?, ?, ?> stmtContext, final Class<DT> type) {
101         final ImmutableList.Builder<StmtContext<AT, DT, ?>> listBuilder = ImmutableList.builder();
102         listBuilder.addAll(findAllDeclaredSubstatements(stmtContext, type));
103         listBuilder.addAll(findAllEffectiveSubstatements(stmtContext, type));
104         return listBuilder.build();
105     }
106
107     @SuppressWarnings("unchecked")
108     public static <AT, DT extends DeclaredStatement<AT>> StmtContext<AT, ?, ?> findFirstEffectiveSubstatement(
109             final StmtContext<?, ?, ?> stmtContext, final Class<DT> declaredType) {
110         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.effectiveSubstatements()) {
111             if (producesDeclared(subStmtContext, declaredType)) {
112                 return (StmtContext<AT, ?, ?>) subStmtContext;
113             }
114         }
115         return null;
116     }
117
118     /**
119      * Searches for the first substatement of the specified type in the specified statement context.
120      * First, it tries to find the substatement in the effective substatements of the statement context.
121      * If it was not found, then it proceeds to search in the declared substatements. If it still was not found,
122      * the method returns null.
123      *
124      * @param stmtContext statement context to search in
125      * @param declaredType substatement type to search for
126      * @param <AT> statement argument type
127      * @param <DT> declared statement type
128      * @return statement context that was searched for or null if was not found
129      */
130     public static <AT, DT extends DeclaredStatement<AT>> StmtContext<AT, ?, ?> findFirstSubstatement(
131             final StmtContext<?, ?, ?> stmtContext, final Class<DT> declaredType) {
132         final StmtContext<AT, ?, ?> effectiveSubstatement = findFirstEffectiveSubstatement(stmtContext, declaredType);
133         return effectiveSubstatement != null ? effectiveSubstatement : findFirstDeclaredSubstatement(stmtContext,
134                 declaredType);
135     }
136
137     @SafeVarargs
138     public static StmtContext<?, ?, ?> findFirstDeclaredSubstatement(final StmtContext<?, ?, ?> stmtContext,
139             int startIndex, final Class<? extends DeclaredStatement<?>>... types) {
140         if (startIndex >= types.length) {
141             return null;
142         }
143
144         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
145             if (producesDeclared(subStmtContext, types[startIndex])) {
146                 return startIndex + 1 == types.length ? subStmtContext : findFirstDeclaredSubstatement(subStmtContext,
147                         ++startIndex, types);
148             }
149         }
150         return null;
151     }
152
153     public static <DT extends DeclaredStatement<?>> StmtContext<?, ?, ?> findFirstDeclaredSubstatementOnSublevel(
154             final StmtContext<?, ?, ?> stmtContext, final Class<DT> declaredType, int sublevel) {
155         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
156             if (sublevel == 1 && producesDeclared(subStmtContext, declaredType)) {
157                 return subStmtContext;
158             }
159             if (sublevel > 1) {
160                 final StmtContext<?, ?, ?> result = findFirstDeclaredSubstatementOnSublevel(subStmtContext,
161                         declaredType, --sublevel);
162                 if (result != null) {
163                     return result;
164                 }
165             }
166         }
167
168         return null;
169     }
170
171     public static <DT extends DeclaredStatement<?>> StmtContext<?, ?, ?> findDeepFirstDeclaredSubstatement(
172             final StmtContext<?, ?, ?> stmtContext, final Class<DT> declaredType) {
173         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
174             if (producesDeclared(subStmtContext, declaredType)) {
175                 return subStmtContext;
176             }
177
178             final StmtContext<?, ?, ?> result = findDeepFirstDeclaredSubstatement(subStmtContext, declaredType);
179             if (result != null) {
180                 return result;
181             }
182         }
183
184         return null;
185     }
186
187     public static boolean producesDeclared(final StmtContext<?, ?, ?> ctx,
188             final Class<? extends DeclaredStatement<?>> type) {
189         return type.isAssignableFrom(ctx.getPublicDefinition().getDeclaredRepresentationClass());
190     }
191
192     public static boolean isInExtensionBody(final StmtContext<?, ?, ?> stmtCtx) {
193         StmtContext<?, ?, ?> current = stmtCtx;
194         while (!current.getParentContext().isRootContext()) {
195             current = current.getParentContext();
196             if (producesDeclared(current, UnknownStatementImpl.class)) {
197                 return true;
198             }
199         }
200
201         return false;
202     }
203
204     public static boolean isUnknownStatement(final StmtContext<?, ?, ?> stmtCtx) {
205         return producesDeclared(stmtCtx, UnknownStatementImpl.class);
206     }
207
208     public static Collection<SchemaNodeIdentifier> replaceModuleQNameForKey(
209             final StmtContext<Collection<SchemaNodeIdentifier>, KeyStatement, ?> keyStmtCtx,
210             final QNameModule newQNameModule) {
211
212         final Builder<SchemaNodeIdentifier> builder = ImmutableSet.builder();
213         boolean replaced = false;
214         for (final SchemaNodeIdentifier arg : keyStmtCtx.getStatementArgument()) {
215             final QName qname = arg.getLastComponent();
216             if (!newQNameModule.equals(qname)) {
217                 final QName newQname = keyStmtCtx.getFromNamespace(QNameCacheNamespace.class,
218                         QName.create(newQNameModule, qname.getLocalName()));
219                 builder.add(SchemaNodeIdentifier.create(false, newQname));
220                 replaced = true;
221             } else {
222                 builder.add(arg);
223             }
224         }
225
226         // This makes sure we reuse the collection when a grouping is
227         // instantiated in the same module
228         return replaced ? builder.build() : keyStmtCtx.getStatementArgument();
229     }
230
231     public static boolean areFeaturesSupported(final StmtContext.Mutable<?, ?, ?> stmtContext) {
232         switch (stmtContext.getSupportedByFeatures()) {
233         case SUPPORTED:
234             return true;
235         case NOT_SUPPORTED:
236             return false;
237         default:
238             break;
239         }
240
241         final Set<QName> supportedFeatures = stmtContext.getFromNamespace(SupportedFeaturesNamespace.class,
242                 SupportedFeatures.SUPPORTED_FEATURES);
243         /*
244          * If set of supported features has not been provided, all features are
245          * supported by default.
246          */
247         if (supportedFeatures == null) {
248             stmtContext.setSupportedByFeatures(true);
249             return true;
250         }
251
252         final boolean result = checkFeatureSupport(stmtContext, supportedFeatures);
253         stmtContext.setSupportedByFeatures(result);
254         return result;
255     }
256
257     private static boolean checkFeatureSupport(final StmtContext.Mutable<?, ?, ?> stmtContext,
258             final Set<QName> supportedFeatures) {
259         boolean isSupported = false;
260         boolean containsIfFeature = false;
261         for (final StatementContextBase<?, ?, ?> stmt : stmtContext.declaredSubstatements()) {
262             if (YangStmtMapping.IF_FEATURE.equals(stmt.getPublicDefinition())) {
263                 containsIfFeature = true;
264                 if (((Predicate<Set<QName>>) stmt.getStatementArgument()).test(supportedFeatures)) {
265                     isSupported = true;
266                 } else {
267                     isSupported = false;
268                     break;
269                 }
270             }
271         }
272
273         return !containsIfFeature || isSupported;
274     }
275
276     /**
277      * Checks whether statement context is a presence container or not.
278      *
279      * @param stmtCtx
280      *            statement context
281      * @return true if it is a presence container
282      */
283     public static boolean isPresenceContainer(final StatementContextBase<?, ?, ?> stmtCtx) {
284         return stmtCtx.getPublicDefinition() == YangStmtMapping.CONTAINER && containsPresenceSubStmt(stmtCtx);
285     }
286
287     /**
288      * Checks whether statement context is a non-presence container or not.
289      *
290      * @param stmtCtx
291      *            statement context
292      * @return true if it is a non-presence container
293      */
294     public static boolean isNonPresenceContainer(final StatementContextBase<?, ?, ?> stmtCtx) {
295         return stmtCtx.getPublicDefinition() == YangStmtMapping.CONTAINER && !containsPresenceSubStmt(stmtCtx);
296     }
297
298     private static boolean containsPresenceSubStmt(final StatementContextBase<?, ?, ?> stmtCtx) {
299         return findFirstSubstatement(stmtCtx, PresenceStatement.class) != null;
300     }
301
302     /**
303      * Checks whether statement context is a mandatory node according to RFC6020
304      * or not.
305      *
306      * @param stmtCtx
307      *            statement context
308      * @return true if it is a mandatory node according to RFC6020
309      */
310     public static boolean isMandatoryNode(final StatementContextBase<?, ?, ?> stmtCtx) {
311         return isMandatoryListOrLeafList(stmtCtx) || isMandatoryLeafChoiceOrAnyXML(stmtCtx);
312     }
313
314     private static boolean isMandatoryLeafChoiceOrAnyXML(final StatementContextBase<?, ?, ?> stmtCtx) {
315         if (!(stmtCtx.getPublicDefinition() instanceof YangStmtMapping)) {
316             return false;
317         }
318         switch ((YangStmtMapping) stmtCtx.getPublicDefinition()) {
319         case LEAF:
320         case CHOICE:
321         case ANYXML:
322             return Boolean.TRUE.equals(firstSubstatementAttributeOf(stmtCtx, MandatoryStatement.class));
323         default:
324             return false;
325         }
326     }
327
328     private static boolean isMandatoryListOrLeafList(final StatementContextBase<?, ?, ?> stmtCtx) {
329         if (!(stmtCtx.getPublicDefinition() instanceof YangStmtMapping)) {
330             return false;
331         }
332         switch ((YangStmtMapping) stmtCtx.getPublicDefinition()) {
333         case LIST:
334         case LEAF_LIST:
335             final Integer minElements = firstSubstatementAttributeOf(stmtCtx, MinElementsStatement.class);
336             return minElements != null && minElements > 0;
337         default:
338             return false;
339         }
340     }
341
342     /**
343      * Checks whether at least one ancestor of a StatementContext matches one
344      * from collection of statement definitions.
345      *
346      * @param ctx
347      *            StatementContext to be checked
348      * @param ancestorTypes
349      *            collection of statement definitions
350      *
351      * @return true if at least one ancestor of a StatementContext matches one
352      *         from collection of statement definitions, otherwise false.
353      */
354     public static boolean hasAncestorOfType(final StmtContext<?, ?, ?> ctx,
355             final Collection<StatementDefinition> ancestorTypes) {
356         Preconditions.checkNotNull(ctx);
357         Preconditions.checkNotNull(ancestorTypes);
358         StmtContext<?, ?, ?> current = ctx.getParentContext();
359         while (current != null) {
360             if (ancestorTypes.contains(current.getPublicDefinition())) {
361                 return true;
362             }
363             current = current.getParentContext();
364         }
365         return false;
366     }
367
368     /**
369      * Checks whether all of StmtContext's ancestors of specified type have a child of specified type
370      *
371      * @param ctx StmtContext to be checked
372      * @param ancestorType type of ancestor to search for
373      * @param ancestorChildType type of child to search for in the specified ancestor type
374      *
375      * @return true if all of StmtContext's ancestors of specified type have a child of specified type, otherwise false
376      */
377     public static <AT, DT extends DeclaredStatement<AT>> boolean hasAncestorOfTypeWithChildOfType(final StmtContext<?, ?, ?> ctx,
378             final StatementDefinition ancestorType, final StatementDefinition ancestorChildType) {
379         Preconditions.checkNotNull(ctx);
380         Preconditions.checkNotNull(ancestorType);
381         Preconditions.checkNotNull(ancestorChildType);
382         StmtContext<?, ?, ?> current = ctx.getParentContext();
383         while (!(current instanceof RootStatementContext)) {
384             if (ancestorType.equals(current.getPublicDefinition())) {
385                 @SuppressWarnings("unchecked")
386                 final Class<DT> ancestorChildTypeClass = (Class<DT>) ancestorChildType.getDeclaredRepresentationClass();
387                 if (findFirstSubstatement(current, ancestorChildTypeClass) == null) {
388                     return false;
389                 }
390             }
391             current = current.getParentContext();
392         }
393
394         return true;
395     }
396
397     /**
398      * Checks whether the parent of StmtContext is of specified type
399      *
400      * @param ctx
401      *            StmtContext to be checked
402      * @param parentType
403      *            type of parent to check
404      *
405      * @return true if the parent of StmtContext is of specified type, otherwise
406      *         false
407      */
408     public static boolean hasParentOfType(final StmtContext<?, ?, ?> ctx, final StatementDefinition parentType) {
409         Preconditions.checkNotNull(ctx);
410         Preconditions.checkNotNull(parentType);
411         final StmtContext<?, ?, ?> parentContext = ctx.getParentContext();
412         return parentContext != null ? parentType.equals(parentContext.getPublicDefinition()) : false;
413     }
414 }