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