Propagate @Nonnull and @Nullable annotations
[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.Splitter;
11 import com.google.common.collect.ImmutableList;
12 import com.google.common.collect.ImmutableSet;
13 import com.google.common.collect.ImmutableSet.Builder;
14 import java.util.Collection;
15 import java.util.function.Predicate;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.common.QNameModule;
18 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
19 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
20 import org.opendaylight.yangtools.yang.model.api.stmt.KeyStatement;
21 import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
22 import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsStatement;
23 import org.opendaylight.yangtools.yang.model.api.stmt.PresenceStatement;
24 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
25 import org.opendaylight.yangtools.yang.model.repo.api.IfFeaturePredicates;
26 import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace;
27 import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace.SupportedFeatures;
28 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
29 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.UnknownStatementImpl;
30
31 public final class StmtContextUtils {
32     public static final Splitter LIST_KEY_SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults();
33
34     private StmtContextUtils() {
35         throw new UnsupportedOperationException("Utility class");
36     }
37
38     @SuppressWarnings("unchecked")
39     public static <AT, DT extends DeclaredStatement<AT>> AT firstAttributeOf(
40             final Iterable<? extends StmtContext<?, ?, ?>> contexts, final Class<DT> declaredType) {
41         for (final StmtContext<?, ?, ?> ctx : contexts) {
42             if (producesDeclared(ctx, declaredType)) {
43                 return (AT) ctx.getStatementArgument();
44             }
45         }
46         return null;
47     }
48
49     @SuppressWarnings("unchecked")
50     public static <AT, DT extends DeclaredStatement<AT>> AT firstAttributeOf(final StmtContext<?, ?, ?> ctx,
51             final Class<DT> declaredType) {
52         return producesDeclared(ctx, declaredType) ? (AT) ctx.getStatementArgument() : null;
53     }
54
55     public static <AT, DT extends DeclaredStatement<AT>> AT firstSubstatementAttributeOf(
56             final StmtContext<?, ?, ?> ctx, final Class<DT> declaredType) {
57         final AT firstAttribute = firstAttributeOf(ctx.effectiveSubstatements(), declaredType);
58         return firstAttribute != null ? firstAttribute : firstAttributeOf(ctx.declaredSubstatements(), declaredType);
59     }
60
61     @SuppressWarnings("unchecked")
62     public static <AT, DT extends DeclaredStatement<AT>> StmtContext<AT, ?, ?> findFirstDeclaredSubstatement(
63             final StmtContext<?, ?, ?> stmtContext, final Class<DT> declaredType) {
64         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
65             if (producesDeclared(subStmtContext, declaredType)) {
66                 return (StmtContext<AT, ?, ?>) subStmtContext;
67             }
68         }
69         return null;
70     }
71
72     @SuppressWarnings("unchecked")
73     public static <AT, DT extends DeclaredStatement<AT>> Collection<StmtContext<AT, DT, ?>> findAllDeclaredSubstatements(
74             final StmtContext<?, ?, ?> stmtContext, final Class<DT> declaredType) {
75         final ImmutableList.Builder<StmtContext<AT, DT, ?>> listBuilder = ImmutableList.builder();
76         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
77             if (producesDeclared(subStmtContext, declaredType)) {
78                 listBuilder.add((StmtContext<AT, DT, ?>) subStmtContext);
79             }
80         }
81         return listBuilder.build();
82     }
83
84     @SuppressWarnings("unchecked")
85     public static <AT, DT extends DeclaredStatement<AT>> Collection<StmtContext<AT, DT, ?>> findAllEffectiveSubstatements(
86             final StmtContext<?, ?, ?> stmtContext, final Class<DT> type) {
87         final ImmutableList.Builder<StmtContext<AT, DT, ?>> listBuilder = ImmutableList.builder();
88         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.effectiveSubstatements()) {
89             if (producesDeclared(subStmtContext, type)) {
90                 listBuilder.add((StmtContext<AT, DT, ?>) subStmtContext);
91             }
92         }
93         return listBuilder.build();
94     }
95
96     public static <AT, DT extends DeclaredStatement<AT>> Collection<StmtContext<AT, DT, ?>> findAllSubstatements(
97             final StmtContext<?, ?, ?> stmtContext, final Class<DT> type) {
98         final ImmutableList.Builder<StmtContext<AT, DT, ?>> listBuilder = ImmutableList.builder();
99         listBuilder.addAll(findAllDeclaredSubstatements(stmtContext, type));
100         listBuilder.addAll(findAllEffectiveSubstatements(stmtContext, type));
101         return listBuilder.build();
102     }
103
104     @SuppressWarnings("unchecked")
105     public static <AT, DT extends DeclaredStatement<AT>> StmtContext<AT, ?, ?> findFirstEffectiveSubstatement(
106             final StmtContext<?, ?, ?> stmtContext, final Class<DT> declaredType) {
107         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.effectiveSubstatements()) {
108             if (producesDeclared(subStmtContext, declaredType)) {
109                 return (StmtContext<AT, ?, ?>) subStmtContext;
110             }
111         }
112         return null;
113     }
114
115     public static <AT, DT extends DeclaredStatement<AT>> StmtContext<AT, ?, ?> findFirstSubstatement(
116             final StmtContext<?, ?, ?> stmtContext, final Class<DT> declaredType) {
117         final StmtContext<AT, ?, ?> declaredSubstatement = findFirstDeclaredSubstatement(stmtContext, declaredType);
118         return declaredSubstatement != null ? declaredSubstatement : findFirstEffectiveSubstatement(stmtContext,
119                 declaredType);
120     }
121
122     @SafeVarargs
123     public static StmtContext<?, ?, ?> findFirstDeclaredSubstatement(final StmtContext<?, ?, ?> stmtContext,
124             int startIndex, final Class<? extends DeclaredStatement<?>>... types) {
125         if (startIndex >= types.length) {
126             return null;
127         }
128
129         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
130             if (producesDeclared(subStmtContext, types[startIndex])) {
131                 return startIndex + 1 == types.length ? subStmtContext : findFirstDeclaredSubstatement(subStmtContext,
132                         ++startIndex, types);
133             }
134         }
135         return null;
136     }
137
138     public static <DT extends DeclaredStatement<?>> StmtContext<?, ?, ?> findFirstDeclaredSubstatementOnSublevel(
139             final StmtContext<?, ?, ?> stmtContext, final Class<DT> declaredType, int sublevel) {
140         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
141             if (sublevel == 1 && producesDeclared(subStmtContext, declaredType)) {
142                 return subStmtContext;
143             }
144             if (sublevel > 1) {
145                 final StmtContext<?, ?, ?> result = findFirstDeclaredSubstatementOnSublevel(subStmtContext,
146                         declaredType, --sublevel);
147                 if (result != null) {
148                     return result;
149                 }
150             }
151         }
152
153         return null;
154     }
155
156     public static <DT extends DeclaredStatement<?>> StmtContext<?, ?, ?> findDeepFirstDeclaredSubstatement(
157             final StmtContext<?, ?, ?> stmtContext, final Class<DT> declaredType) {
158         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
159             if (producesDeclared(subStmtContext, declaredType)) {
160                 return subStmtContext;
161             }
162
163             final StmtContext<?, ?, ?> result = findDeepFirstDeclaredSubstatement(subStmtContext, declaredType);
164             if (result != null) {
165                 return result;
166             }
167         }
168
169         return null;
170     }
171
172     public static boolean producesDeclared(final StmtContext<?, ?, ?> ctx,
173             final Class<? extends DeclaredStatement<?>> type) {
174         return type.isAssignableFrom(ctx.getPublicDefinition().getDeclaredRepresentationClass());
175     }
176
177     public static boolean isInExtensionBody(final StmtContext<?, ?, ?> stmtCtx) {
178         StmtContext<?, ?, ?> current = stmtCtx;
179         while (!current.getParentContext().isRootContext()) {
180             current = current.getParentContext();
181             if (producesDeclared(current, UnknownStatementImpl.class)) {
182                 return true;
183             }
184         }
185
186         return false;
187     }
188
189     public static boolean isUnknownStatement(final StmtContext<?, ?, ?> stmtCtx) {
190         return producesDeclared(stmtCtx, UnknownStatementImpl.class);
191     }
192
193     public static Collection<SchemaNodeIdentifier> replaceModuleQNameForKey(
194             final StmtContext<Collection<SchemaNodeIdentifier>, KeyStatement, ?> keyStmtCtx,
195             final QNameModule newQNameModule) {
196
197         final Builder<SchemaNodeIdentifier> builder = ImmutableSet.builder();
198         boolean replaced = false;
199         for (final SchemaNodeIdentifier arg : keyStmtCtx.getStatementArgument()) {
200             final QName qname = arg.getLastComponent();
201             if (!newQNameModule.equals(qname)) {
202                 final QName newQname = keyStmtCtx.getFromNamespace(QNameCacheNamespace.class,
203                         QName.create(newQNameModule, qname.getLocalName()));
204                 builder.add(SchemaNodeIdentifier.create(false, newQname));
205                 replaced = true;
206             } else {
207                 builder.add(arg);
208             }
209         }
210
211         // This makes sure we reuse the collection when a grouping is
212         // instantiated in the same module
213         return replaced ? builder.build() : keyStmtCtx.getStatementArgument();
214     }
215
216     public static boolean areFeaturesSupported(final StmtContext.Mutable<?, ?, ?> stmtContext) {
217         switch (stmtContext.getSupportedByFeatures()) {
218         case SUPPORTED:
219             return true;
220         case NOT_SUPPORTED:
221             return false;
222         default:
223             break;
224         }
225
226         final Predicate<QName> isFeatureSupported = stmtContext.getFromNamespace(SupportedFeaturesNamespace.class,
227                 SupportedFeatures.SUPPORTED_FEATURES);
228         if (IfFeaturePredicates.ALL_FEATURES.equals(isFeatureSupported)) {
229             stmtContext.setSupportedByFeatures(true);
230             return true;
231         }
232
233         final boolean result = checkFeatureSupport(stmtContext, isFeatureSupported);
234         stmtContext.setSupportedByFeatures(result);
235         return result;
236     }
237
238     private static boolean checkFeatureSupport(final StmtContext.Mutable<?, ?, ?> stmtContext,
239             final Predicate<QName> isFeatureSupported) {
240         final Collection<StatementContextBase<?, ?, ?>> substatements = stmtContext.declaredSubstatements();
241
242         boolean isSupported = false;
243         boolean containsIfFeature = false;
244         for (final StatementContextBase<?, ?, ?> stmt : substatements) {
245             if (stmt.getPublicDefinition().equals(Rfc6020Mapping.IF_FEATURE)) {
246                 containsIfFeature = true;
247                 if (isFeatureSupported.test((QName) stmt.getStatementArgument())) {
248                     isSupported = true;
249                 } else {
250                     isSupported = false;
251                     break;
252                 }
253             }
254         }
255
256         return !containsIfFeature || isSupported;
257     }
258
259     /**
260      * Checks whether statement context is a presence container or not.
261      *
262      * @param stmtCtx
263      *            statement context
264      * @return true if it is a presence container
265      */
266     public static boolean isPresenceContainer(final StatementContextBase<?, ?, ?> stmtCtx) {
267         return stmtCtx.getPublicDefinition() == Rfc6020Mapping.CONTAINER && containsPresenceSubStmt(stmtCtx);
268     }
269
270     /**
271      * Checks whether statement context is a non-presence container or not.
272      *
273      * @param stmtCtx
274      *            statement context
275      * @return true if it is a non-presence container
276      */
277     public static boolean isNonPresenceContainer(final StatementContextBase<?, ?, ?> stmtCtx) {
278         return stmtCtx.getPublicDefinition() == Rfc6020Mapping.CONTAINER && !containsPresenceSubStmt(stmtCtx);
279     }
280
281     private static boolean containsPresenceSubStmt(final StatementContextBase<?, ?, ?> stmtCtx) {
282         return findFirstSubstatement(stmtCtx, PresenceStatement.class) != null;
283     }
284
285     /**
286      * Checks whether statement context is a mandatory node according to RFC6020
287      * or not.
288      *
289      * @param stmtCtx
290      *            statement context
291      * @return true if it is a mandatory node according to RFC6020
292      */
293     public static boolean isMandatoryNode(final StatementContextBase<?, ?, ?> stmtCtx) {
294         return isMandatoryListOrLeafList(stmtCtx) || isMandatoryLeafChoiceOrAnyXML(stmtCtx);
295     }
296
297     private static boolean isMandatoryLeafChoiceOrAnyXML(final StatementContextBase<?, ?, ?> stmtCtx) {
298         if (!(stmtCtx.getPublicDefinition() instanceof Rfc6020Mapping)) {
299             return false;
300         }
301         switch ((Rfc6020Mapping) stmtCtx.getPublicDefinition()) {
302         case LEAF:
303         case CHOICE:
304         case ANYXML:
305             return Boolean.TRUE.equals(firstSubstatementAttributeOf(stmtCtx, MandatoryStatement.class));
306         default:
307             return false;
308         }
309     }
310
311     private static boolean isMandatoryListOrLeafList(final StatementContextBase<?, ?, ?> stmtCtx) {
312         if (!(stmtCtx.getPublicDefinition() instanceof Rfc6020Mapping)) {
313             return false;
314         }
315         switch ((Rfc6020Mapping) stmtCtx.getPublicDefinition()) {
316         case LIST:
317         case LEAF_LIST:
318             final Integer minElements = firstSubstatementAttributeOf(stmtCtx, MinElementsStatement.class);
319             return minElements != null && minElements > 0;
320         default:
321             return false;
322         }
323     }
324 }