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