Reformulate StatementContextFactory.createEffective()
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / submodule / SubmoduleEffectiveStatementImpl.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.rfc7950.stmt.submodule;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf;
12
13 import com.google.common.collect.ImmutableList;
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.ImmutableMap.Builder;
16 import com.google.common.collect.ImmutableSet;
17 import com.google.common.collect.Iterables;
18 import com.google.common.collect.Maps;
19 import java.util.Collection;
20 import java.util.HashSet;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.Optional;
24 import java.util.Set;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.opendaylight.yangtools.yang.common.QNameModule;
27 import org.opendaylight.yangtools.yang.common.Revision;
28 import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
29 import org.opendaylight.yangtools.yang.model.api.Submodule;
30 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
31 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
32 import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement;
33 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
34 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement.PrefixToEffectiveModuleNamespace;
35 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement.QNameModuleToPrefixNamespace;
36 import org.opendaylight.yangtools.yang.model.api.stmt.RevisionEffectiveStatement;
37 import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
38 import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
39 import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveModule;
40 import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current;
41 import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement;
42 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
43 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
44 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
45 import org.opendaylight.yangtools.yang.parser.spi.source.IncludedSubmoduleNameToModuleCtx;
46 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName;
47 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
48
49 final class SubmoduleEffectiveStatementImpl
50         extends AbstractEffectiveModule<SubmoduleStatement, SubmoduleEffectiveStatement>
51         implements Submodule, SubmoduleEffectiveStatement, MutableStatement {
52     private final ImmutableMap<String, ModuleEffectiveStatement> prefixToModule;
53     private final ImmutableMap<QNameModule, String> namespaceToPrefix;
54     private final QNameModule qnameModule;
55
56     private Set<StmtContext<?, SubmoduleStatement, SubmoduleEffectiveStatement>> submoduleContexts;
57     private ImmutableSet<Submodule> submodules;
58     private boolean sealed;
59
60     SubmoduleEffectiveStatementImpl(final Current<UnqualifiedQName, SubmoduleStatement> stmt,
61             final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
62         super(stmt, substatements, findSubmodulePrefix(stmt.caerbannog()));
63
64         final QNameModule belongsToModuleQName = stmt.getFromNamespace(ModuleNameToModuleQName.class,
65             firstAttributeOf(stmt.caerbannog().declaredSubstatements(), BelongsToStatement.class));
66
67         final Builder<String, ModuleEffectiveStatement> prefixToModuleBuilder = ImmutableMap.builder();
68         appendPrefixes(stmt, prefixToModuleBuilder);
69         prefixToModule = prefixToModuleBuilder.build();
70
71         final Map<QNameModule, String> tmp = Maps.newLinkedHashMapWithExpectedSize(prefixToModule.size());
72         for (Entry<String, ModuleEffectiveStatement> e : prefixToModule.entrySet()) {
73             tmp.putIfAbsent(e.getValue().localQNameModule(), e.getKey());
74         }
75         namespaceToPrefix = ImmutableMap.copyOf(tmp);
76
77         final Optional<Revision> submoduleRevision = findFirstEffectiveSubstatementArgument(
78             RevisionEffectiveStatement.class);
79         this.qnameModule = QNameModule.create(belongsToModuleQName.getNamespace(), submoduleRevision).intern();
80
81         /*
82          * Because of possible circular chains of includes between submodules we can
83          * collect only submodule contexts here and then build them during
84          * sealing of this statement.
85          */
86         final Map<String, StmtContext<?, ?, ?>> includedSubmodulesMap = stmt.getAllFromCurrentStmtCtxNamespace(
87             IncludedSubmoduleNameToModuleCtx.class);
88         if (includedSubmodulesMap != null) {
89             final Set<StmtContext<?, SubmoduleStatement, SubmoduleEffectiveStatement>> submoduleContextsInit =
90                 new HashSet<>();
91             for (final StmtContext<?, ?, ?> submoduleCtx : includedSubmodulesMap.values()) {
92                 submoduleContextsInit.add(
93                     (StmtContext<?, SubmoduleStatement, SubmoduleEffectiveStatement>)submoduleCtx);
94             }
95             submoduleContexts = ImmutableSet.copyOf(submoduleContextsInit);
96         } else {
97             submoduleContexts = ImmutableSet.of();
98         }
99
100         if (!submoduleContexts.isEmpty()) {
101             ((Mutable<?, ?, ?>) stmt.caerbannog()).addMutableStmtToSeal(this);
102             sealed = false;
103         } else {
104             submodules = ImmutableSet.of();
105             sealed = true;
106         }
107     }
108
109     @Override
110     public QNameModule getQNameModule() {
111         return qnameModule;
112     }
113
114     @Override
115     @SuppressWarnings("unchecked")
116     public <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
117             final @NonNull Class<N> namespace) {
118         if (PrefixToEffectiveModuleNamespace.class.equals(namespace)) {
119             return Optional.of((Map<K, V>) prefixToModule);
120         }
121         if (QNameModuleToPrefixNamespace.class.equals(namespace)) {
122             return Optional.of((Map<K, V>) namespaceToPrefix);
123         }
124         return super.getNamespaceContents(namespace);
125     }
126
127     @Override
128     public Collection<? extends @NonNull Submodule> getSubmodules() {
129         checkState(sealed, "Attempt to get base submodules from unsealed submodule effective statement %s",
130             qnameModule);
131         return submodules;
132     }
133
134     @Override
135     public SubmoduleEffectiveStatement asEffectiveStatement() {
136         return this;
137     }
138
139     @Override
140     public void seal() {
141         if (!sealed) {
142             submodules = ImmutableSet.copyOf(Iterables.transform(submoduleContexts,
143                 ctx -> (Submodule) ctx.buildEffective()));
144             submoduleContexts = ImmutableSet.of();
145             sealed = true;
146         }
147     }
148
149     private static @NonNull String findSubmodulePrefix(final StmtContext<UnqualifiedQName, ?, ?> ctx) {
150         final String name = ctx.coerceRawStatementArgument();
151         final StmtContext<?, ?, ?> belongsTo = SourceException.throwIfNull(
152                 StmtContextUtils.findFirstDeclaredSubstatement(ctx, BelongsToStatement.class),
153                 ctx.getStatementSourceReference(), "Unable to find belongs-to statement in submodule %s.", name);
154         return findPrefix(belongsTo, "submodule", name);
155     }
156 }