Teach YANG parser to retain DeclarationReference
[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.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.parser.api.YangParserConfiguration;
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.DeclaredStatements;
44 import org.opendaylight.yangtools.yang.model.spi.meta.SubstatementIndexingException;
45 import org.opendaylight.yangtools.yang.parser.spi.ModuleNamespace;
46 import org.opendaylight.yangtools.yang.parser.spi.NamespaceToModule;
47 import org.opendaylight.yangtools.yang.parser.spi.PreLinkageModuleNamespace;
48 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
49 import org.opendaylight.yangtools.yang.parser.spi.meta.CommonStmtCtx;
50 import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current;
51 import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionModuleNamespace;
52 import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionNamespace;
53 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
54 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
55 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
56 import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
57 import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToNamespace;
58 import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
59 import org.opendaylight.yangtools.yang.parser.spi.source.IncludedSubmoduleNameToModuleCtx;
60 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
61 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToSourceIdentifier;
62 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName;
63 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToNamespace;
64 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNamespaceForBelongsTo;
65 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleQNameToModuleName;
66 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
67 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
68
69 @Beta
70 public final class ModuleStatementSupport
71         extends AbstractStatementSupport<UnqualifiedQName, ModuleStatement, ModuleEffectiveStatement> {
72     private static final SubstatementValidator RFC6020_VALIDATOR = SubstatementValidator.builder(YangStmtMapping.MODULE)
73         .addAny(YangStmtMapping.ANYXML)
74         .addAny(YangStmtMapping.AUGMENT)
75         .addAny(YangStmtMapping.CHOICE)
76         .addOptional(YangStmtMapping.CONTACT)
77         .addAny(YangStmtMapping.CONTAINER)
78         .addOptional(YangStmtMapping.DESCRIPTION)
79         .addAny(YangStmtMapping.DEVIATION)
80         .addAny(YangStmtMapping.EXTENSION)
81         .addAny(YangStmtMapping.FEATURE)
82         .addAny(YangStmtMapping.GROUPING)
83         .addAny(YangStmtMapping.IDENTITY)
84         .addAny(YangStmtMapping.IMPORT)
85         .addAny(YangStmtMapping.INCLUDE)
86         .addAny(YangStmtMapping.LEAF)
87         .addAny(YangStmtMapping.LEAF_LIST)
88         .addAny(YangStmtMapping.LIST)
89         .addMandatory(YangStmtMapping.NAMESPACE)
90         .addAny(YangStmtMapping.NOTIFICATION)
91         .addOptional(YangStmtMapping.ORGANIZATION)
92         .addMandatory(YangStmtMapping.PREFIX)
93         .addOptional(YangStmtMapping.REFERENCE)
94         .addAny(YangStmtMapping.REVISION)
95         .addAny(YangStmtMapping.RPC)
96         .addAny(YangStmtMapping.TYPEDEF)
97         .addAny(YangStmtMapping.USES)
98         .addOptional(YangStmtMapping.YANG_VERSION)
99         .addOptional(OpenConfigStatements.OPENCONFIG_VERSION)
100         .build();
101     private static final SubstatementValidator RFC7950_VALIDATOR = 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 YangParserConfiguration config, final SubstatementValidator validator) {
135         super(YangStmtMapping.MODULE, StatementPolicy.reject(), config);
136         this.validator = requireNonNull(validator);
137     }
138
139     public static @NonNull ModuleStatementSupport rfc6020Instance(final YangParserConfiguration config) {
140         return new ModuleStatementSupport(config, RFC6020_VALIDATOR);
141     }
142
143     public static @NonNull ModuleStatementSupport rfc7950Instance(final YangParserConfiguration config) {
144         return new ModuleStatementSupport(config, RFC7950_VALIDATOR);
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 XMLNamespace 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 XMLNamespace 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         if (substatements.isEmpty()) {
254             throw noNamespace(ctx);
255         }
256         return DeclaredStatements.createModule(ctx.getRawArgument(), ctx.getArgument(), substatements);
257     }
258
259     @Override
260     protected ModuleEffectiveStatement createEffective(final Current<UnqualifiedQName, ModuleStatement> stmt,
261             final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
262         if (substatements.isEmpty()) {
263             throw noNamespace(stmt);
264         }
265
266         final List<Submodule> submodules = new ArrayList<>();
267         for (StmtContext<?, ?, ?> submoduleCtx : submoduleContexts(stmt)) {
268             final EffectiveStatement<?, ?> submodule = submoduleCtx.buildEffective();
269             verify(submodule instanceof Submodule, "Submodule statement %s is not a Submodule", submodule);
270             submodules.add((Submodule) submodule);
271         }
272
273         final QNameModule qnameModule = verifyNotNull(stmt.namespaceItem(QNameModuleNamespace.class,
274             Empty.getInstance()));
275         try {
276             return new ModuleEffectiveStatementImpl(stmt, substatements, submodules, qnameModule);
277         } catch (SubstatementIndexingException e) {
278             throw new SourceException(e.getMessage(), stmt, e);
279         }
280     }
281
282     private static Collection<StmtContext<?, ?, ?>> submoduleContexts(final Current<?, ?> stmt) {
283         final Map<String, StmtContext<?, ?, ?>> submodules = stmt.localNamespacePortion(
284             IncludedSubmoduleNameToModuleCtx.class);
285         return submodules == null ? List.of() : submodules.values();
286     }
287
288     private static SourceException noNamespace(final @NonNull CommonStmtCtx stmt) {
289         return new SourceException("No namespace declared in module", stmt);
290     }
291
292     private static void addToSemVerModuleNamespace(
293             final Mutable<UnqualifiedQName, ModuleStatement, ModuleEffectiveStatement> stmt,
294             final SourceIdentifier moduleIdentifier) {
295         final SemVerSourceIdentifier id = SemVerSourceIdentifier.create(stmt.getRawArgument(),
296             stmt.getFromNamespace(SemanticVersionNamespace.class, stmt));
297         stmt.addToNs(SemanticVersionModuleNamespace.class, id, stmt);
298     }
299 }