Simplify Module/Submodule statement argument usage
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / module / AbstractModuleStatementSupport.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.module;
9
10 import static com.google.common.base.Verify.verify;
11 import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf;
12
13 import com.google.common.collect.ImmutableList;
14 import java.net.URI;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Optional;
20 import org.opendaylight.yangtools.yang.common.QNameModule;
21 import org.opendaylight.yangtools.yang.common.Revision;
22 import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
23 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
24 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.Submodule;
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.EffectiveStatement;
29 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
30 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
31 import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
32 import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
33 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
34 import org.opendaylight.yangtools.yang.model.repo.api.SemVerSourceIdentifier;
35 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
36 import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.BaseStatementSupport;
37 import org.opendaylight.yangtools.yang.parser.spi.ModuleNamespace;
38 import org.opendaylight.yangtools.yang.parser.spi.NamespaceToModule;
39 import org.opendaylight.yangtools.yang.parser.spi.PreLinkageModuleNamespace;
40 import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionModuleNamespace;
41 import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionNamespace;
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.ImpPrefixToNamespace;
46 import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
47 import org.opendaylight.yangtools.yang.parser.spi.source.IncludedSubmoduleNameToModuleCtx;
48 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
49 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToSourceIdentifier;
50 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName;
51 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToNamespace;
52 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNamespaceForBelongsTo;
53 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleQNameToModuleName;
54 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
55 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
56
57 abstract class AbstractModuleStatementSupport
58         extends BaseStatementSupport<UnqualifiedQName, ModuleStatement, ModuleEffectiveStatement> {
59     AbstractModuleStatementSupport() {
60         super(YangStmtMapping.MODULE);
61     }
62
63     @Override
64     public final UnqualifiedQName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
65         try {
66             return UnqualifiedQName.of(value);
67         } catch (IllegalArgumentException e) {
68             throw new SourceException(e.getMessage(), ctx.getStatementSourceReference(), e);
69         }
70     }
71
72     @Override
73     public final void onPreLinkageDeclared(
74             final Mutable<UnqualifiedQName, ModuleStatement, ModuleEffectiveStatement> stmt) {
75         final String moduleName = stmt.coerceRawStatementArgument();
76
77         final URI moduleNs = firstAttributeOf(stmt.declaredSubstatements(), NamespaceStatement.class);
78         SourceException.throwIfNull(moduleNs, stmt.getStatementSourceReference(),
79             "Namespace of the module [%s] is missing", moduleName);
80         stmt.addToNs(ModuleNameToNamespace.class, moduleName, moduleNs);
81
82         final String modulePrefix = firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class);
83         SourceException.throwIfNull(modulePrefix, stmt.getStatementSourceReference(),
84             "Prefix of the module [%s] is missing", moduleName);
85         stmt.addToNs(ImpPrefixToNamespace.class, modulePrefix, moduleNs);
86
87         stmt.addContext(PreLinkageModuleNamespace.class, moduleName, stmt);
88
89         final Optional<Revision> revisionDate = StmtContextUtils.getLatestRevision(stmt.declaredSubstatements());
90         final QNameModule qNameModule = QNameModule.create(moduleNs, revisionDate.orElse(null)).intern();
91
92         stmt.addToNs(ModuleCtxToModuleQName.class, stmt, qNameModule);
93         stmt.setRootIdentifier(RevisionSourceIdentifier.create(stmt.coerceStatementArgument().getLocalName(),
94             revisionDate));
95     }
96
97     @Override
98     public final void onLinkageDeclared(
99             final Mutable<UnqualifiedQName, ModuleStatement, ModuleEffectiveStatement> stmt) {
100
101         final Optional<URI> moduleNs = Optional.ofNullable(firstAttributeOf(stmt.declaredSubstatements(),
102                 NamespaceStatement.class));
103         SourceException.throwIf(!moduleNs.isPresent(), stmt.getStatementSourceReference(),
104             "Namespace of the module [%s] is missing", stmt.getStatementArgument());
105
106         final Optional<Revision> revisionDate = StmtContextUtils.getLatestRevision(stmt.declaredSubstatements());
107         final QNameModule qNameModule = QNameModule.create(moduleNs.get(), revisionDate.orElse(null)).intern();
108         final StmtContext<?, ModuleStatement, ModuleEffectiveStatement> possibleDuplicateModule =
109                 stmt.getFromNamespace(NamespaceToModule.class, qNameModule);
110         if (possibleDuplicateModule != null && possibleDuplicateModule != stmt) {
111             throw new SourceException(stmt.getStatementSourceReference(), "Module namespace collision: %s. At %s",
112                     qNameModule.getNamespace(), possibleDuplicateModule.getStatementSourceReference());
113         }
114
115         final String moduleName = stmt.coerceRawStatementArgument();
116         final SourceIdentifier moduleIdentifier = RevisionSourceIdentifier.create(moduleName, revisionDate);
117
118         stmt.addContext(ModuleNamespace.class, moduleIdentifier, stmt);
119         stmt.addContext(ModuleNamespaceForBelongsTo.class, moduleIdentifier.getName(), stmt);
120         stmt.addContext(NamespaceToModule.class, qNameModule, stmt);
121
122         final String modulePrefix = firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class);
123         SourceException.throwIfNull(modulePrefix, stmt.getStatementSourceReference(),
124             "Prefix of the module [%s] is missing", stmt.getStatementArgument());
125
126         stmt.addToNs(PrefixToModule.class, modulePrefix, qNameModule);
127         stmt.addToNs(ModuleNameToModuleQName.class, moduleName, qNameModule);
128         stmt.addToNs(ModuleCtxToModuleQName.class, stmt, qNameModule);
129         stmt.addToNs(ModuleCtxToSourceIdentifier.class, stmt, moduleIdentifier);
130         stmt.addToNs(ModuleQNameToModuleName.class, qNameModule, moduleName);
131         stmt.addToNs(ImportPrefixToModuleCtx.class, modulePrefix, stmt);
132
133         if (stmt.isEnabledSemanticVersioning()) {
134             addToSemVerModuleNamespace(stmt, moduleIdentifier);
135         }
136     }
137
138     @Override
139     protected final ImmutableList<? extends EffectiveStatement<?, ?>> buildEffectiveSubstatements(
140             final StmtContext<UnqualifiedQName, ModuleStatement, ModuleEffectiveStatement> ctx,
141             final List<? extends StmtContext<?, ?, ?>> substatements) {
142         final ImmutableList<? extends EffectiveStatement<?, ?>> local =
143                 super.buildEffectiveSubstatements(ctx, substatements);
144         final Collection<StmtContext<?, ?, ?>> submodules = submoduleContexts(ctx);
145         if (submodules.isEmpty()) {
146             return local;
147         }
148
149         // Concatenate statements so they appear as if they were part of target module
150         final List<EffectiveStatement<?, ?>> others = new ArrayList<>();
151         for (StmtContext<?, ?, ?> submoduleCtx : submodules) {
152             for (EffectiveStatement<?, ?> effective : submoduleCtx.buildEffective().effectiveSubstatements()) {
153                 if (effective instanceof SchemaNode || effective instanceof DataNodeContainer) {
154                     others.add(effective);
155                 }
156             }
157         }
158
159         return ImmutableList.<EffectiveStatement<?, ?>>builderWithExpectedSize(local.size() + others.size())
160                 .addAll(local)
161                 .addAll(others)
162                 .build();
163     }
164
165     @Override
166     protected final ModuleStatement createDeclared(final StmtContext<UnqualifiedQName, ModuleStatement, ?> ctx,
167             final ImmutableList<? extends DeclaredStatement<?>> substatements) {
168         return new ModuleStatementImpl(ctx, substatements);
169     }
170
171     @Override
172     protected final ModuleStatement createEmptyDeclared(final StmtContext<UnqualifiedQName, ModuleStatement, ?> ctx) {
173         throw noNamespace(ctx);
174     }
175
176     @Override
177     protected final ModuleEffectiveStatement createEffective(
178             final StmtContext<UnqualifiedQName, ModuleStatement, ModuleEffectiveStatement> ctx,
179             final ModuleStatement declared, final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
180         final List<Submodule> submodules = new ArrayList<>();
181         for (StmtContext<?, ?, ?> submoduleCtx : submoduleContexts(ctx)) {
182             final EffectiveStatement<?, ?> submodule = submoduleCtx.buildEffective();
183             verify(submodule instanceof Submodule, "Submodule statement %s is not a Submodule", submodule);
184             submodules.add((Submodule) submodule);
185         }
186
187         return new ModuleEffectiveStatementImpl(ctx, declared, substatements, submodules);
188     }
189
190     @Override
191     protected final ModuleEffectiveStatement createEmptyEffective(
192             final StmtContext<UnqualifiedQName, ModuleStatement, ModuleEffectiveStatement> ctx,
193             final ModuleStatement declared) {
194         throw noNamespace(ctx);
195     }
196
197     private static Collection<StmtContext<?, ?, ?>> submoduleContexts(final StmtContext<?, ?, ?> ctx) {
198         final Map<String, StmtContext<?, ?, ?>> submodules = ctx.getAllFromCurrentStmtCtxNamespace(
199             IncludedSubmoduleNameToModuleCtx.class);
200         return submodules == null ? List.of() : submodules.values();
201     }
202
203     private static SourceException noNamespace(final StmtContext<?, ?, ?> ctx) {
204         return new SourceException("No namespace declared in module", ctx.getStatementSourceReference());
205     }
206
207     private static void addToSemVerModuleNamespace(
208             final Mutable<UnqualifiedQName, ModuleStatement, ModuleEffectiveStatement> stmt,
209             final SourceIdentifier moduleIdentifier) {
210         final SemVerSourceIdentifier id = SemVerSourceIdentifier.create(stmt.coerceRawStatementArgument(),
211             stmt.getFromNamespace(SemanticVersionNamespace.class, stmt));
212         stmt.addToNs(SemanticVersionModuleNamespace.class, id, stmt);
213     }
214 }