Cleanup use of Guava library
[yangtools.git] / yang / yang-parser-spi / 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 static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.base.Splitter;
14 import com.google.common.base.Strings;
15 import com.google.common.collect.ImmutableList;
16 import java.util.Collection;
17 import java.util.Date;
18 import java.util.Optional;
19 import java.util.Set;
20 import java.util.function.Predicate;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.common.QNameModule;
23 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
24 import org.opendaylight.yangtools.yang.common.YangVersion;
25 import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
26 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
27 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
28 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
29 import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement;
30 import org.opendaylight.yangtools.yang.model.api.stmt.KeyStatement;
31 import org.opendaylight.yangtools.yang.model.api.stmt.LeafStatement;
32 import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
33 import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsStatement;
34 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
35 import org.opendaylight.yangtools.yang.model.api.stmt.PresenceStatement;
36 import org.opendaylight.yangtools.yang.model.api.stmt.RevisionStatement;
37 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
38 import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
39 import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
40 import org.opendaylight.yangtools.yang.model.api.stmt.UnrecognizedStatement;
41 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
42 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
43 import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleName;
44 import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToModuleIdentifier;
45 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
46 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleIdentifierToModuleQName;
47 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName;
48 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
49
50 public final class StmtContextUtils {
51     public static final Splitter LIST_KEY_SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults();
52
53     private StmtContextUtils() {
54         throw new UnsupportedOperationException("Utility class");
55     }
56
57     @SuppressWarnings("unchecked")
58     public static <A, D extends DeclaredStatement<A>> A firstAttributeOf(
59             final Iterable<? extends StmtContext<?, ?, ?>> contexts, final Class<D> declaredType) {
60         for (final StmtContext<?, ?, ?> ctx : contexts) {
61             if (producesDeclared(ctx, declaredType)) {
62                 return (A) ctx.getStatementArgument();
63             }
64         }
65         return null;
66     }
67
68     @SuppressWarnings("unchecked")
69     public static <A, D extends DeclaredStatement<A>> A firstAttributeOf(final StmtContext<?, ?, ?> ctx,
70             final Class<D> declaredType) {
71         return producesDeclared(ctx, declaredType) ? (A) ctx.getStatementArgument() : null;
72     }
73
74     public static <A, D extends DeclaredStatement<A>> A firstSubstatementAttributeOf(
75             final StmtContext<?, ?, ?> ctx, final Class<D> declaredType) {
76         return firstAttributeOf(ctx.allSubstatements(), declaredType);
77     }
78
79     @SuppressWarnings("unchecked")
80     public static <A, D extends DeclaredStatement<A>> StmtContext<A, ?, ?> findFirstDeclaredSubstatement(
81             final StmtContext<?, ?, ?> stmtContext, final Class<D> declaredType) {
82         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
83             if (producesDeclared(subStmtContext, declaredType)) {
84                 return (StmtContext<A, ?, ?>) subStmtContext;
85             }
86         }
87         return null;
88     }
89
90     @SafeVarargs
91     public static StmtContext<?, ?, ?> findFirstDeclaredSubstatement(final StmtContext<?, ?, ?> stmtContext,
92             int startIndex, final Class<? extends DeclaredStatement<?>>... types) {
93         if (startIndex >= types.length) {
94             return null;
95         }
96
97         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
98             if (producesDeclared(subStmtContext, types[startIndex])) {
99                 return startIndex + 1 == types.length ? subStmtContext : findFirstDeclaredSubstatement(subStmtContext,
100                         ++startIndex, types);
101             }
102         }
103         return null;
104     }
105
106     @SuppressWarnings("unchecked")
107     public static <A, D extends DeclaredStatement<A>> Collection<StmtContext<A, D, ?>> findAllDeclaredSubstatements(
108             final StmtContext<?, ?, ?> stmtContext, final Class<D> declaredType) {
109         final ImmutableList.Builder<StmtContext<A, D, ?>> listBuilder = ImmutableList.builder();
110         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
111             if (producesDeclared(subStmtContext, declaredType)) {
112                 listBuilder.add((StmtContext<A, D, ?>) subStmtContext);
113             }
114         }
115         return listBuilder.build();
116     }
117
118     @SuppressWarnings("unchecked")
119     public static <A, D extends DeclaredStatement<A>> Collection<StmtContext<A, D, ?>> findAllEffectiveSubstatements(
120             final StmtContext<?, ?, ?> stmtContext, final Class<D> type) {
121         final ImmutableList.Builder<StmtContext<A, D, ?>> listBuilder = ImmutableList.builder();
122         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.effectiveSubstatements()) {
123             if (producesDeclared(subStmtContext, type)) {
124                 listBuilder.add((StmtContext<A, D, ?>) subStmtContext);
125             }
126         }
127         return listBuilder.build();
128     }
129
130     public static <A, D extends DeclaredStatement<A>> Collection<StmtContext<A, D, ?>> findAllSubstatements(
131             final StmtContext<?, ?, ?> stmtContext, final Class<D> type) {
132         final ImmutableList.Builder<StmtContext<A, D, ?>> listBuilder = ImmutableList.builder();
133         listBuilder.addAll(findAllDeclaredSubstatements(stmtContext, type));
134         listBuilder.addAll(findAllEffectiveSubstatements(stmtContext, type));
135         return listBuilder.build();
136     }
137
138     @SuppressWarnings("unchecked")
139     public static <A, D extends DeclaredStatement<A>> StmtContext<A, ?, ?> findFirstEffectiveSubstatement(
140             final StmtContext<?, ?, ?> stmtContext, final Class<D> declaredType) {
141         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.effectiveSubstatements()) {
142             if (producesDeclared(subStmtContext, declaredType)) {
143                 return (StmtContext<A, ?, ?>) subStmtContext;
144             }
145         }
146         return null;
147     }
148
149     /**
150      * Searches for the first substatement of the specified type in the specified statement context.
151      * First, it tries to find the substatement in the effective substatements of the statement context.
152      * If it was not found, then it proceeds to search in the declared substatements. If it still was not found,
153      * the method returns null.
154      *
155      * @param stmtContext statement context to search in
156      * @param declaredType substatement type to search for
157      * @param <A> statement argument type
158      * @param <D> declared statement type
159      * @return statement context that was searched for or null if was not found
160      */
161     public static <A, D extends DeclaredStatement<A>> StmtContext<A, ?, ?> findFirstSubstatement(
162             final StmtContext<?, ?, ?> stmtContext, final Class<D> declaredType) {
163         final StmtContext<A, ?, ?> effectiveSubstatement = findFirstEffectiveSubstatement(stmtContext, declaredType);
164         return effectiveSubstatement != null ? effectiveSubstatement : findFirstDeclaredSubstatement(stmtContext,
165                 declaredType);
166     }
167
168     public static <D extends DeclaredStatement<?>> StmtContext<?, ?, ?> findFirstDeclaredSubstatementOnSublevel(
169             final StmtContext<?, ?, ?> stmtContext, final Class<D> declaredType, int sublevel) {
170         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
171             if (sublevel == 1 && producesDeclared(subStmtContext, declaredType)) {
172                 return subStmtContext;
173             }
174             if (sublevel > 1) {
175                 final StmtContext<?, ?, ?> result = findFirstDeclaredSubstatementOnSublevel(subStmtContext,
176                         declaredType, --sublevel);
177                 if (result != null) {
178                     return result;
179                 }
180             }
181         }
182
183         return null;
184     }
185
186     public static <D extends DeclaredStatement<?>> StmtContext<?, ?, ?> findDeepFirstDeclaredSubstatement(
187             final StmtContext<?, ?, ?> stmtContext, final Class<D> declaredType) {
188         for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
189             if (producesDeclared(subStmtContext, declaredType)) {
190                 return subStmtContext;
191             }
192
193             final StmtContext<?, ?, ?> result = findDeepFirstDeclaredSubstatement(subStmtContext, declaredType);
194             if (result != null) {
195                 return result;
196             }
197         }
198
199         return null;
200     }
201
202     public static boolean producesDeclared(final StmtContext<?, ?, ?> ctx,
203             final Class<? extends DeclaredStatement<?>> type) {
204         return type.isAssignableFrom(ctx.getPublicDefinition().getDeclaredRepresentationClass());
205     }
206
207     public static boolean isInExtensionBody(final StmtContext<?, ?, ?> stmtCtx) {
208         StmtContext<?, ?, ?> current = stmtCtx;
209         while (current.getParentContext().getParentContext() != null) {
210             current = current.getParentContext();
211             if (isUnknownStatement(current)) {
212                 return true;
213             }
214         }
215
216         return false;
217     }
218
219     /**
220      * Returns true if supplied statement context represents unknown statement,
221      * otherwise returns false.
222      *
223      * @param stmtCtx
224      *            statement context to be checked
225      * @return true if supplied statement context represents unknown statement,
226      *         otherwise false
227      * @throws NullPointerException
228      *             if supplied statement context is null
229      */
230     public static boolean isUnknownStatement(final StmtContext<?, ?, ?> stmtCtx) {
231         return UnknownStatement.class
232                 .isAssignableFrom(stmtCtx.getPublicDefinition().getDeclaredRepresentationClass());
233     }
234
235     /**
236      * Returns true if supplied statement context represents unrecognized
237      * statement, otherwise returns false.
238      *
239      * @param stmtCtx
240      *            statement context to be checked
241      * @return true if supplied statement context represents unrecognized
242      *         statement, otherwise false
243      * @throws NullPointerException
244      *             if supplied statement context is null
245      */
246     public static boolean isUnrecognizedStatement(final StmtContext<?, ?, ?> stmtCtx) {
247         return UnrecognizedStatement.class
248                 .isAssignableFrom(stmtCtx.getPublicDefinition().getDeclaredRepresentationClass());
249     }
250
251     public static boolean checkFeatureSupport(final StmtContext<?, ?, ?> stmtContext,
252             final Set<QName> supportedFeatures) {
253         boolean isSupported = false;
254         boolean containsIfFeature = false;
255         for (final StmtContext<?, ?, ?> stmt : stmtContext.declaredSubstatements()) {
256             if (YangStmtMapping.IF_FEATURE.equals(stmt.getPublicDefinition())) {
257                 containsIfFeature = true;
258                 if (((Predicate<Set<QName>>) stmt.getStatementArgument()).test(supportedFeatures)) {
259                     isSupported = true;
260                 } else {
261                     isSupported = false;
262                     break;
263                 }
264             }
265         }
266
267         return !containsIfFeature || isSupported;
268     }
269
270     /**
271      * Checks whether statement context is a presence container or not.
272      *
273      * @param stmtCtx
274      *            statement context
275      * @return true if it is a presence container
276      */
277     public static boolean isPresenceContainer(final StmtContext<?, ?, ?> stmtCtx) {
278         return stmtCtx.getPublicDefinition() == YangStmtMapping.CONTAINER && containsPresenceSubStmt(stmtCtx);
279     }
280
281     /**
282      * Checks whether statement context is a non-presence container or not.
283      *
284      * @param stmtCtx
285      *            statement context
286      * @return true if it is a non-presence container
287      */
288     public static boolean isNonPresenceContainer(final StmtContext<?, ?, ?> stmtCtx) {
289         return stmtCtx.getPublicDefinition() == YangStmtMapping.CONTAINER && !containsPresenceSubStmt(stmtCtx);
290     }
291
292     private static boolean containsPresenceSubStmt(final StmtContext<?, ?, ?> stmtCtx) {
293         return findFirstSubstatement(stmtCtx, PresenceStatement.class) != null;
294     }
295
296     /**
297      * Checks whether statement context is a mandatory leaf, choice, anyxml,
298      * list or leaf-list according to RFC6020 or not.
299      *
300      * @param stmtCtx
301      *            statement context
302      * @return true if it is a mandatory leaf, choice, anyxml, list or leaf-list
303      *         according to RFC6020.
304      */
305     public static boolean isMandatoryNode(final StmtContext<?, ?, ?> stmtCtx) {
306         if (!(stmtCtx.getPublicDefinition() instanceof YangStmtMapping)) {
307             return false;
308         }
309         switch ((YangStmtMapping) stmtCtx.getPublicDefinition()) {
310             case LEAF:
311             case CHOICE:
312             case ANYXML:
313                 return Boolean.TRUE.equals(firstSubstatementAttributeOf(stmtCtx, MandatoryStatement.class));
314             case LIST:
315             case LEAF_LIST:
316                 final Integer minElements = firstSubstatementAttributeOf(stmtCtx, MinElementsStatement.class);
317                 return minElements != null && minElements > 0;
318             default:
319                 return false;
320         }
321     }
322
323     /**
324      * Checks whether a statement context is a statement of supplied statement
325      * definition and whether it is not mandatory leaf, choice, anyxml, list or
326      * leaf-list according to RFC6020.
327      *
328      * @param stmtCtx
329      *            statement context
330      * @param stmtDef
331      *            statement definition
332      * @return true if supplied statement context is a statement of supplied
333      *         statement definition and if it is not mandatory leaf, choice,
334      *         anyxml, list or leaf-list according to RFC6020
335      */
336     public static boolean isNotMandatoryNodeOfType(final StmtContext<?, ?, ?> stmtCtx,
337             final StatementDefinition stmtDef) {
338         return stmtCtx.getPublicDefinition().equals(stmtDef) && !isMandatoryNode(stmtCtx);
339     }
340
341     /**
342      * Checks whether at least one ancestor of a StatementContext matches one from a collection of statement
343      * definitions.
344      *
345      * @param ctx
346      *            StatementContext to be checked
347      * @param ancestorTypes
348      *            collection of statement definitions
349      * @return true if at least one ancestor of a StatementContext matches one
350      *         from collection of statement definitions, otherwise false.
351      */
352     public static boolean hasAncestorOfType(final StmtContext<?, ?, ?> ctx,
353             final Collection<StatementDefinition> ancestorTypes) {
354         requireNonNull(ancestorTypes);
355         StmtContext<?, ?, ?> current = ctx.getParentContext();
356         while (current != null) {
357             if (ancestorTypes.contains(current.getPublicDefinition())) {
358                 return true;
359             }
360             current = current.getParentContext();
361         }
362         return false;
363     }
364
365     /**
366      * Checks whether all of StmtContext's ancestors of specified type have a child of specified type.
367      *
368      * @param ctx StmtContext to be checked
369      * @param ancestorType type of ancestor to search for
370      * @param ancestorChildType type of child to search for in the specified ancestor type
371      * @return true if all of StmtContext's ancestors of specified type have a child of specified type, otherwise false
372      */
373     public static <A, D extends DeclaredStatement<A>> boolean hasAncestorOfTypeWithChildOfType(
374             final StmtContext<?, ?, ?> ctx, final StatementDefinition ancestorType,
375             final StatementDefinition ancestorChildType) {
376         requireNonNull(ctx);
377         requireNonNull(ancestorType);
378         requireNonNull(ancestorChildType);
379
380         StmtContext<?, ?, ?> current = ctx.getParentContext();
381         StmtContext<?, ?, ?> parent = current.getParentContext();
382         while (parent != null) {
383             if (ancestorType.equals(current.getPublicDefinition())) {
384                 @SuppressWarnings("unchecked")
385                 final Class<D> ancestorChildTypeClass = (Class<D>) ancestorChildType.getDeclaredRepresentationClass();
386                 if (findFirstSubstatement(current, ancestorChildTypeClass) == null) {
387                     return false;
388                 }
389             }
390
391             current = parent;
392             parent = current.getParentContext();
393         }
394
395         return true;
396     }
397
398     /**
399      * Checks whether the parent of StmtContext is of specified type.
400      *
401      * @param ctx
402      *            StmtContext to be checked
403      * @param parentType
404      *            type of parent to check
405      * @return true if the parent of StmtContext is of specified type, otherwise false
406      */
407     public static boolean hasParentOfType(final StmtContext<?, ?, ?> ctx, final StatementDefinition parentType) {
408         requireNonNull(parentType);
409         final StmtContext<?, ?, ?> parentContext = ctx.getParentContext();
410         return parentContext != null ? parentType.equals(parentContext.getPublicDefinition()) : false;
411     }
412
413     /**
414      * Validates the specified statement context with regards to if-feature and when statement on list keys.
415      * The context can either be a leaf which is defined directly in the substatements of a keyed list or a uses
416      * statement defined in a keyed list (a uses statement may add leaves into the list).
417      *
418      * <p>
419      * If one of the list keys contains an if-feature or a when statement in YANG 1.1 model, an exception is thrown.
420      *
421      * @param ctx statement context to be validated
422      */
423     public static void validateIfFeatureAndWhenOnListKeys(final StmtContext<?, ?, ?> ctx) {
424         if (!isRelevantForIfFeatureAndWhenOnListKeysCheck(ctx)) {
425             return;
426         }
427
428         final StmtContext<?, ?, ?> listStmtCtx = ctx.getParentContext();
429         final StmtContext<Collection<SchemaNodeIdentifier>, ?, ?> keyStmtCtx =
430                 StmtContextUtils.findFirstDeclaredSubstatement(listStmtCtx, KeyStatement.class);
431
432         if (YangStmtMapping.LEAF.equals(ctx.getPublicDefinition())) {
433             if (isListKey(ctx, keyStmtCtx)) {
434                 disallowIfFeatureAndWhenOnListKeys(ctx);
435             }
436         } else if (YangStmtMapping.USES.equals(ctx.getPublicDefinition())) {
437             StmtContextUtils.findAllEffectiveSubstatements(listStmtCtx, LeafStatement.class).forEach(leafStmtCtx -> {
438                 if (isListKey(leafStmtCtx, keyStmtCtx)) {
439                     disallowIfFeatureAndWhenOnListKeys(leafStmtCtx);
440                 }
441             });
442         }
443     }
444
445     private static boolean isRelevantForIfFeatureAndWhenOnListKeysCheck(final StmtContext<?, ?, ?> ctx) {
446         return YangVersion.VERSION_1_1.equals(ctx.getRootVersion())
447                 && StmtContextUtils.hasParentOfType(ctx, YangStmtMapping.LIST)
448                 && StmtContextUtils.findFirstDeclaredSubstatement(ctx.getParentContext(), KeyStatement.class) != null;
449     }
450
451     private static boolean isListKey(final StmtContext<?, ?, ?> leafStmtCtx,
452             final StmtContext<Collection<SchemaNodeIdentifier>, ?, ?> keyStmtCtx) {
453         for (final SchemaNodeIdentifier keyIdentifier : keyStmtCtx.getStatementArgument()) {
454             if (leafStmtCtx.getStatementArgument().equals(keyIdentifier.getLastComponent())) {
455                 return true;
456             }
457         }
458
459         return false;
460     }
461
462     private static void disallowIfFeatureAndWhenOnListKeys(final StmtContext<?, ?, ?> leafStmtCtx) {
463         leafStmtCtx.allSubstatements().forEach(leafSubstmtCtx -> {
464             final StatementDefinition statementDef = leafSubstmtCtx.getPublicDefinition();
465             SourceException.throwIf(YangStmtMapping.IF_FEATURE.equals(statementDef)
466                     || YangStmtMapping.WHEN.equals(statementDef), leafStmtCtx.getStatementSourceReference(),
467                     "%s statement is not allowed in %s leaf statement which is specified as a list key.",
468                     statementDef.getStatementName(), leafStmtCtx.getStatementArgument());
469         });
470     }
471
472     public static QName qnameFromArgument(StmtContext<?, ?, ?> ctx, final String value) {
473         if (Strings.isNullOrEmpty(value)) {
474             return ctx.getPublicDefinition().getStatementName();
475         }
476
477         String prefix;
478         QNameModule qnameModule = null;
479         String localName = null;
480
481         final String[] namesParts = value.split(":");
482         switch (namesParts.length) {
483             case 1:
484                 localName = namesParts[0];
485                 qnameModule = StmtContextUtils.getRootModuleQName(ctx);
486                 break;
487             default:
488                 prefix = namesParts[0];
489                 localName = namesParts[1];
490                 qnameModule = StmtContextUtils.getModuleQNameByPrefix(ctx, prefix);
491                 // in case of unknown statement argument, we're not going to parse it
492                 if (qnameModule == null && isUnknownStatement(ctx)) {
493                     localName = value;
494                     qnameModule = StmtContextUtils.getRootModuleQName(ctx);
495                 }
496                 if (qnameModule == null && ctx.getCopyHistory().getLastOperation() == CopyType.ADDED_BY_AUGMENTATION) {
497                     ctx = ctx.getOriginalCtx().orElse(null);
498                     qnameModule = StmtContextUtils.getModuleQNameByPrefix(ctx, prefix);
499                 }
500                 break;
501         }
502
503         qnameModule = InferenceException.throwIfNull(qnameModule, ctx.getStatementSourceReference(),
504             "Cannot resolve QNameModule for '%s'", value);
505
506         final QNameModule resultQNameModule;
507         if (qnameModule.getRevision() == null) {
508             resultQNameModule = QNameModule.create(qnameModule.getNamespace(), SimpleDateFormatUtil.DEFAULT_DATE_REV)
509                 .intern();
510         } else {
511             resultQNameModule = qnameModule;
512         }
513
514         return ctx.getFromNamespace(QNameCacheNamespace.class, QName.create(resultQNameModule, localName));
515     }
516
517     public static QNameModule getRootModuleQName(final StmtContext<?, ?, ?> ctx) {
518         if (ctx == null) {
519             return null;
520         }
521
522         final StmtContext<?, ?, ?> rootCtx = ctx.getRoot();
523         final QNameModule qnameModule;
524
525         if (producesDeclared(rootCtx, ModuleStatement.class)) {
526             qnameModule = rootCtx.getFromNamespace(ModuleCtxToModuleQName.class, rootCtx);
527         } else if (producesDeclared(rootCtx, SubmoduleStatement.class)) {
528             final String belongsToModuleName = firstAttributeOf(rootCtx.declaredSubstatements(),
529                 BelongsToStatement.class);
530             qnameModule = rootCtx.getFromNamespace(ModuleNameToModuleQName.class, belongsToModuleName);
531         } else {
532             qnameModule = null;
533         }
534
535         checkArgument(qnameModule != null, "Failed to look up root QNameModule for %s", ctx);
536         if (qnameModule.getRevision() != null) {
537             return qnameModule;
538         }
539
540         return QNameModule.create(qnameModule.getNamespace(), SimpleDateFormatUtil.DEFAULT_DATE_REV).intern();
541     }
542
543     public static QNameModule getModuleQNameByPrefix(final StmtContext<?, ?, ?> ctx, final String prefix) {
544         final ModuleIdentifier modId = ctx.getRoot().getFromNamespace(ImpPrefixToModuleIdentifier.class, prefix);
545         final QNameModule qnameModule = ctx.getFromNamespace(ModuleIdentifierToModuleQName.class, modId);
546
547         if (qnameModule == null && producesDeclared(ctx.getRoot(), SubmoduleStatement.class)) {
548             final String moduleName = ctx.getRoot().getFromNamespace(BelongsToPrefixToModuleName.class, prefix);
549             return ctx.getFromNamespace(ModuleNameToModuleQName.class, moduleName);
550         }
551         return qnameModule;
552     }
553
554     public static SourceIdentifier createSourceIdentifier(final StmtContext<?, ?, ?> root) {
555         final QNameModule qNameModule = root.getFromNamespace(ModuleCtxToModuleQName.class, root);
556         if (qNameModule != null) {
557             // creates SourceIdentifier for a module
558             return RevisionSourceIdentifier.create((String) root.getStatementArgument(),
559                 qNameModule.getFormattedRevision());
560         }
561
562         // creates SourceIdentifier for a submodule
563         final Date revision = Optional.ofNullable(getLatestRevision(root.declaredSubstatements()))
564                 .orElse(SimpleDateFormatUtil.DEFAULT_DATE_REV);
565         final String formattedRevision = SimpleDateFormatUtil.getRevisionFormat().format(revision);
566         return RevisionSourceIdentifier.create((String) root.getStatementArgument(), formattedRevision);
567     }
568
569     public static Date getLatestRevision(final Iterable<? extends StmtContext<?, ?, ?>> subStmts) {
570         Date revision = null;
571         for (final StmtContext<?, ?, ?> subStmt : subStmts) {
572             if (subStmt.getPublicDefinition().getDeclaredRepresentationClass().isAssignableFrom(
573                     RevisionStatement.class)) {
574                 if (revision == null && subStmt.getStatementArgument() != null) {
575                     revision = (Date) subStmt.getStatementArgument();
576                 } else if (subStmt.getStatementArgument() != null
577                         && ((Date) subStmt.getStatementArgument()).compareTo(revision) > 0) {
578                     revision = (Date) subStmt.getStatementArgument();
579                 }
580             }
581         }
582         return revision;
583     }
584 }