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