Merge ModuleStatementSupport
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / module / ModuleStatementSupport.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 java.util.Objects.requireNonNull;
12 import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf;
13
14 import com.google.common.annotations.Beta;
15 import com.google.common.collect.ImmutableList;
16 import java.net.URI;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Optional;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.opendaylight.yangtools.openconfig.model.api.OpenConfigStatements;
24 import org.opendaylight.yangtools.yang.common.QNameModule;
25 import org.opendaylight.yangtools.yang.common.Revision;
26 import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
27 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
28 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.Submodule;
30 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
31 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
32 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
33 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
34 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
35 import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
36 import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
37 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
38 import org.opendaylight.yangtools.yang.model.repo.api.SemVerSourceIdentifier;
39 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
40 import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.BaseStatementSupport;
41 import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.SubstatementIndexingException;
42 import org.opendaylight.yangtools.yang.parser.spi.ModuleNamespace;
43 import org.opendaylight.yangtools.yang.parser.spi.NamespaceToModule;
44 import org.opendaylight.yangtools.yang.parser.spi.PreLinkageModuleNamespace;
45 import org.opendaylight.yangtools.yang.parser.spi.meta.CommonStmtCtx;
46 import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current;
47 import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionModuleNamespace;
48 import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionNamespace;
49 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
50 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
51 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
52 import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
53 import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToNamespace;
54 import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
55 import org.opendaylight.yangtools.yang.parser.spi.source.IncludedSubmoduleNameToModuleCtx;
56 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
57 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToSourceIdentifier;
58 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName;
59 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToNamespace;
60 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNamespaceForBelongsTo;
61 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleQNameToModuleName;
62 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
63 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
64
65 @Beta
66 public final class ModuleStatementSupport
67         extends BaseStatementSupport<UnqualifiedQName, ModuleStatement, ModuleEffectiveStatement> {
68     private static final @NonNull ModuleStatementSupport RFC6020_INSTANCE = new ModuleStatementSupport(
69         SubstatementValidator.builder(YangStmtMapping.MODULE)
70             .addAny(YangStmtMapping.ANYXML)
71             .addAny(YangStmtMapping.AUGMENT)
72             .addAny(YangStmtMapping.CHOICE)
73             .addOptional(YangStmtMapping.CONTACT)
74             .addAny(YangStmtMapping.CONTAINER)
75             .addOptional(YangStmtMapping.DESCRIPTION)
76             .addAny(YangStmtMapping.DEVIATION)
77             .addAny(YangStmtMapping.EXTENSION)
78             .addAny(YangStmtMapping.FEATURE)
79             .addAny(YangStmtMapping.GROUPING)
80             .addAny(YangStmtMapping.IDENTITY)
81             .addAny(YangStmtMapping.IMPORT)
82             .addAny(YangStmtMapping.INCLUDE)
83             .addAny(YangStmtMapping.LEAF)
84             .addAny(YangStmtMapping.LEAF_LIST)
85             .addAny(YangStmtMapping.LIST)
86             .addMandatory(YangStmtMapping.NAMESPACE)
87             .addAny(YangStmtMapping.NOTIFICATION)
88             .addOptional(YangStmtMapping.ORGANIZATION)
89             .addMandatory(YangStmtMapping.PREFIX)
90             .addOptional(YangStmtMapping.REFERENCE)
91             .addAny(YangStmtMapping.REVISION)
92             .addAny(YangStmtMapping.RPC)
93             .addAny(YangStmtMapping.TYPEDEF)
94             .addAny(YangStmtMapping.USES)
95             .addOptional(YangStmtMapping.YANG_VERSION)
96             .addOptional(OpenConfigStatements.OPENCONFIG_VERSION)
97             .build());
98     private static final @NonNull ModuleStatementSupport RFC7950_INSTANCE = new ModuleStatementSupport(
99         SubstatementValidator.builder(YangStmtMapping.MODULE)
100             .addAny(YangStmtMapping.ANYDATA)
101             .addAny(YangStmtMapping.ANYXML)
102             .addAny(YangStmtMapping.AUGMENT)
103             .addAny(YangStmtMapping.CHOICE)
104             .addOptional(YangStmtMapping.CONTACT)
105             .addAny(YangStmtMapping.CONTAINER)
106             .addOptional(YangStmtMapping.DESCRIPTION)
107             .addAny(YangStmtMapping.DEVIATION)
108             .addAny(YangStmtMapping.EXTENSION)
109             .addAny(YangStmtMapping.FEATURE)
110             .addAny(YangStmtMapping.GROUPING)
111             .addAny(YangStmtMapping.IDENTITY)
112             .addAny(YangStmtMapping.IMPORT)
113             .addAny(YangStmtMapping.INCLUDE)
114             .addAny(YangStmtMapping.LEAF)
115             .addAny(YangStmtMapping.LEAF_LIST)
116             .addAny(YangStmtMapping.LIST)
117             .addMandatory(YangStmtMapping.NAMESPACE)
118             .addAny(YangStmtMapping.NOTIFICATION)
119             .addOptional(YangStmtMapping.ORGANIZATION)
120             .addMandatory(YangStmtMapping.PREFIX)
121             .addOptional(YangStmtMapping.REFERENCE)
122             .addAny(YangStmtMapping.REVISION)
123             .addAny(YangStmtMapping.RPC)
124             .addAny(YangStmtMapping.TYPEDEF)
125             .addAny(YangStmtMapping.USES)
126             .addMandatory(YangStmtMapping.YANG_VERSION)
127             .addOptional(OpenConfigStatements.OPENCONFIG_VERSION)
128             .build());
129
130     private final SubstatementValidator validator;
131
132     private ModuleStatementSupport(final SubstatementValidator validator) {
133         super(YangStmtMapping.MODULE, CopyPolicy.REJECT);
134         this.validator = requireNonNull(validator);
135     }
136
137     public static @NonNull ModuleStatementSupport rfc6020Instance() {
138         return RFC6020_INSTANCE;
139     }
140
141     public static @NonNull ModuleStatementSupport rfc7950Instance() {
142         return RFC7950_INSTANCE;
143     }
144
145     @Override
146     public UnqualifiedQName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
147         try {
148             return UnqualifiedQName.of(value);
149         } catch (IllegalArgumentException e) {
150             throw new SourceException(e.getMessage(), ctx, e);
151         }
152     }
153
154     @Override
155     public void onPreLinkageDeclared(final Mutable<UnqualifiedQName, ModuleStatement, ModuleEffectiveStatement> stmt) {
156         final String moduleName = stmt.getRawArgument();
157
158         final URI moduleNs = SourceException.throwIfNull(
159             firstAttributeOf(stmt.declaredSubstatements(), NamespaceStatement.class), stmt,
160             "Namespace of the module [%s] is missing", moduleName);
161         stmt.addToNs(ModuleNameToNamespace.class, moduleName, moduleNs);
162
163         final String modulePrefix = SourceException.throwIfNull(
164             firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class), stmt,
165             "Prefix of the module [%s] is missing", moduleName);
166         stmt.addToNs(ImpPrefixToNamespace.class, modulePrefix, moduleNs);
167
168         stmt.addContext(PreLinkageModuleNamespace.class, moduleName, stmt);
169
170         final Optional<Revision> revisionDate = StmtContextUtils.getLatestRevision(stmt.declaredSubstatements());
171         final QNameModule qNameModule = QNameModule.create(moduleNs, revisionDate.orElse(null)).intern();
172
173         stmt.addToNs(ModuleCtxToModuleQName.class, stmt, qNameModule);
174         stmt.setRootIdentifier(RevisionSourceIdentifier.create(stmt.getArgument().getLocalName(), revisionDate));
175     }
176
177     @Override
178     public void onLinkageDeclared(final Mutable<UnqualifiedQName, ModuleStatement, ModuleEffectiveStatement> stmt) {
179         final URI moduleNs = SourceException.throwIfNull(
180             firstAttributeOf(stmt.declaredSubstatements(), NamespaceStatement.class), stmt,
181             "Namespace of the module [%s] is missing", stmt.argument());
182
183         final Optional<Revision> revisionDate = StmtContextUtils.getLatestRevision(stmt.declaredSubstatements());
184         final QNameModule qNameModule = QNameModule.create(moduleNs, revisionDate.orElse(null)).intern();
185         final StmtContext<?, ModuleStatement, ModuleEffectiveStatement> possibleDuplicateModule =
186                 stmt.getFromNamespace(NamespaceToModule.class, qNameModule);
187         if (possibleDuplicateModule != null && possibleDuplicateModule != stmt) {
188             throw new SourceException(stmt, "Module namespace collision: %s. At %s", qNameModule.getNamespace(),
189                 possibleDuplicateModule.sourceReference());
190         }
191
192         final String moduleName = stmt.getRawArgument();
193         final SourceIdentifier moduleIdentifier = RevisionSourceIdentifier.create(moduleName, revisionDate);
194
195         stmt.addContext(ModuleNamespace.class, moduleIdentifier, stmt);
196         stmt.addContext(ModuleNamespaceForBelongsTo.class, moduleIdentifier.getName(), stmt);
197         stmt.addContext(NamespaceToModule.class, qNameModule, stmt);
198
199         final String modulePrefix = SourceException.throwIfNull(
200             firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class), stmt,
201             "Prefix of the module [%s] is missing", stmt.argument());
202
203         stmt.addToNs(PrefixToModule.class, modulePrefix, qNameModule);
204         stmt.addToNs(ModuleNameToModuleQName.class, moduleName, qNameModule);
205         stmt.addToNs(ModuleCtxToModuleQName.class, stmt, qNameModule);
206         stmt.addToNs(ModuleCtxToSourceIdentifier.class, stmt, moduleIdentifier);
207         stmt.addToNs(ModuleQNameToModuleName.class, qNameModule, moduleName);
208         stmt.addToNs(ImportPrefixToModuleCtx.class, modulePrefix, stmt);
209
210         if (stmt.isEnabledSemanticVersioning()) {
211             addToSemVerModuleNamespace(stmt, moduleIdentifier);
212         }
213     }
214
215     @Override
216     protected SubstatementValidator getSubstatementValidator() {
217         return validator;
218     }
219
220     @Override
221     protected ImmutableList<? extends EffectiveStatement<?, ?>> buildEffectiveSubstatements(
222             final Current<UnqualifiedQName, ModuleStatement> stmt,
223             final List<? extends StmtContext<?, ?, ?>> substatements) {
224         final ImmutableList<? extends EffectiveStatement<?, ?>> local =
225                 super.buildEffectiveSubstatements(stmt, substatements);
226         final Collection<StmtContext<?, ?, ?>> submodules = submoduleContexts(stmt);
227         if (submodules.isEmpty()) {
228             return local;
229         }
230
231         // Concatenate statements so they appear as if they were part of target module
232         final List<EffectiveStatement<?, ?>> others = new ArrayList<>();
233         for (StmtContext<?, ?, ?> submoduleCtx : submodules) {
234             for (EffectiveStatement<?, ?> effective : submoduleCtx.buildEffective().effectiveSubstatements()) {
235                 if (effective instanceof SchemaNode || effective instanceof DataNodeContainer) {
236                     others.add(effective);
237                 }
238             }
239         }
240
241         return ImmutableList.<EffectiveStatement<?, ?>>builderWithExpectedSize(local.size() + others.size())
242                 .addAll(local)
243                 .addAll(others)
244                 .build();
245     }
246
247     @Override
248     protected ModuleStatement createDeclared(final StmtContext<UnqualifiedQName, ModuleStatement, ?> ctx,
249             final ImmutableList<? extends DeclaredStatement<?>> substatements) {
250         return new ModuleStatementImpl(ctx.getRawArgument(), ctx.getArgument(), substatements);
251     }
252
253     @Override
254     protected ModuleStatement createEmptyDeclared(final StmtContext<UnqualifiedQName, ModuleStatement, ?> ctx) {
255         throw noNamespace(ctx);
256     }
257
258     @Override
259     protected ModuleEffectiveStatement createEffective(final Current<UnqualifiedQName, ModuleStatement> stmt,
260             final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
261         if (substatements.isEmpty()) {
262             throw noNamespace(stmt);
263         }
264
265         final List<Submodule> submodules = new ArrayList<>();
266         for (StmtContext<?, ?, ?> submoduleCtx : submoduleContexts(stmt)) {
267             final EffectiveStatement<?, ?> submodule = submoduleCtx.buildEffective();
268             verify(submodule instanceof Submodule, "Submodule statement %s is not a Submodule", submodule);
269             submodules.add((Submodule) submodule);
270         }
271
272         try {
273             return new ModuleEffectiveStatementImpl(stmt, substatements, submodules);
274         } catch (SubstatementIndexingException e) {
275             throw new SourceException(e.getMessage(), stmt, e);
276         }
277     }
278
279     private static Collection<StmtContext<?, ?, ?>> submoduleContexts(final Current<?, ?> stmt) {
280         final Map<String, StmtContext<?, ?, ?>> submodules = stmt.getAllFromCurrentStmtCtxNamespace(
281             IncludedSubmoduleNameToModuleCtx.class);
282         return submodules == null ? List.of() : submodules.values();
283     }
284
285     private static SourceException noNamespace(final @NonNull CommonStmtCtx stmt) {
286         return new SourceException("No namespace declared in module", stmt);
287     }
288
289     private static void addToSemVerModuleNamespace(
290             final Mutable<UnqualifiedQName, ModuleStatement, ModuleEffectiveStatement> stmt,
291             final SourceIdentifier moduleIdentifier) {
292         final SemVerSourceIdentifier id = SemVerSourceIdentifier.create(stmt.getRawArgument(),
293             stmt.getFromNamespace(SemanticVersionNamespace.class, stmt));
294         stmt.addToNs(SemanticVersionModuleNamespace.class, id, stmt);
295     }
296 }