2 * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.parser.spi;
10 import com.google.common.collect.SetMultimap;
11 import java.util.Collection;
12 import java.util.Optional;
13 import org.eclipse.jdt.annotation.NonNull;
14 import org.opendaylight.yangtools.yang.common.Empty;
15 import org.opendaylight.yangtools.yang.common.QName;
16 import org.opendaylight.yangtools.yang.common.QNameModule;
17 import org.opendaylight.yangtools.yang.common.UnresolvedQName.Unqualified;
18 import org.opendaylight.yangtools.yang.common.XMLNamespace;
19 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
20 import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
21 import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionEffectiveStatement;
22 import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionStatement;
23 import org.opendaylight.yangtools.yang.model.api.stmt.FeatureEffectiveStatement;
24 import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
25 import org.opendaylight.yangtools.yang.model.api.stmt.FeatureStatement;
26 import org.opendaylight.yangtools.yang.model.api.stmt.GroupingEffectiveStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.GroupingStatement;
28 import org.opendaylight.yangtools.yang.model.api.stmt.IdentityEffectiveStatement;
29 import org.opendaylight.yangtools.yang.model.api.stmt.IdentityStatement;
30 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
31 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
32 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
33 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
34 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
35 import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
36 import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
37 import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
38 import org.opendaylight.yangtools.yang.model.api.stmt.TypedefStatement;
39 import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
40 import org.opendaylight.yangtools.yang.parser.spi.meta.ParserNamespace;
41 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
42 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
43 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixResolver;
46 * Baseline {@link ParserNamespace}s mostly derived from YANG specification.
48 public final class ParserNamespaces {
50 * Extension namespace. All extension names defined in a module and its submodules share the same extension
51 * identifier namespace, where each extension is identified by a QName formed from the defining module's QNameModule
52 * and the identifier specified in extension statement's argument.
54 public static final @NonNull ParserNamespace<QName,
55 StmtContext<QName, ExtensionStatement, ExtensionEffectiveStatement>> EXTENSION =
56 new ParserNamespace<>("extension");
59 * Feature namespace. All feature names defined in a module and its submodules share the same feature identifier
60 * namespace. Each feature is identified by a QName formed from the defining module's QNameModule and the feature
63 public static final @NonNull ParserNamespace<QName,
64 StmtContext<QName, FeatureStatement, FeatureEffectiveStatement>> FEATURE = new ParserNamespace<>("feature");
67 * Grouping namespace. * All grouping names defined within a parent node or at the top level of the module
68 * or its submodules share the same grouping identifier namespace. This namespace is scoped to all
69 * descendant nodes of the parent node or module.
72 * This means that any descendant node may use that grouping, and it MUST NOT define a grouping with the same name.
74 public static final @NonNull ParserNamespace<QName,
75 StmtContext<QName, GroupingStatement, GroupingEffectiveStatement>> GROUPING = new ParserNamespace<>("grouping");
78 * Identity namespace. All identity names defined in a module and its submodules share the same identity identifier
81 public static final @NonNull ParserNamespace<QName,
82 StmtContext<QName, IdentityStatement, IdentityEffectiveStatement>> IDENTITY = new ParserNamespace<>("identity");
85 * Module namespace. All modules known to the reactor are populated to this namespace. Each module is identified
86 * by a {@link SourceIdentifier}.
88 public static final @NonNull ParserNamespace<SourceIdentifier,
89 StmtContext<Unqualified, ModuleStatement, ModuleEffectiveStatement>> MODULE = new ParserNamespace<>("module");
92 * Submodule equivalent of {@link #MODULE}.
94 public static final @NonNull ParserNamespace<SourceIdentifier,
95 StmtContext<Unqualified, SubmoduleStatement, SubmoduleEffectiveStatement>> SUBMODULE =
96 new ParserNamespace<>("submodule");
99 * Derived types namespace. All derived type names defined within a parent node or at the top level of the module
100 * (or its submodules) share the same type identifier namespace.
103 * This namespace is scoped to all descendant nodes of the parent node or module. This means that any descendant
104 * node may use that typedef, and it MUST NOT define a typedef with the same name.
107 * This namespace includes all type definitions implied by the language in which the current statement resides
108 * (e.g. RFC6020/RFC7950 for YANG 1.0/1.1).
110 public static final @NonNull ParserNamespace<QName,
111 StmtContext<QName, TypedefStatement, TypedefEffectiveStatement>> TYPE = new ParserNamespace<>("typedef");
114 * A derived namespace allowing lookup of modules based on their {@link QNameModule}.
116 public static final @NonNull ParserNamespace<QNameModule,
117 StmtContext<Unqualified, ModuleStatement, ModuleEffectiveStatement>> NAMESPACE_TO_MODULE =
118 new ParserNamespace<>("namespace-to-module");
121 * Intermediate-stage namespace equivalent to {@link #MODULE} except it is keyed by module names. This namespace is
122 * used to resolve inter-module references before actual linkage occurs.
124 public static final @NonNull ParserNamespace<Unqualified,
125 StmtContext<Unqualified, ModuleStatement, ModuleEffectiveStatement>> PRELINKAGE_MODULE =
126 new ParserNamespace<>("prelinkage-module");
129 * Source-specific mapping of belongsTo prefixes to module identifiers. This mapping allows source-specific context
130 * to correctly populate prefixes map for actual parsing phase and eventually, resolve QName for any valid declared
133 public static final @NonNull ParserNamespace<String, StmtContext<?, ?, ?>> BELONGSTO_PREFIX_TO_MODULECTX =
134 new ParserNamespace<>("belongsto-prefix-to-module");
137 * Source-specific mapping of prefixes to namespaces.
139 // FIXME: bad javadoc
140 public static final @NonNull ParserNamespace<String, Unqualified> BELONGSTO_PREFIX_TO_MODULE_NAME =
141 new ParserNamespace<>("belongsto-prefix-to-name");
144 * Namespace similar to {@link ParserNamespaces#MODULE} for storing modules into Yang model storage but keyed by
147 // FIXME: Better name?
148 public static final @NonNull ParserNamespace<Unqualified,
149 StmtContext<Unqualified, ModuleStatement, ModuleEffectiveStatement>> MODULE_FOR_BELONGSTO =
150 new ParserNamespace<>("module-belongsto");
153 * Pre-linkage source-specific mapping of prefixes to module namespaces.
155 // FIXME: a better name?
156 public static final @NonNull ParserNamespace<String, XMLNamespace> IMP_PREFIX_TO_NAMESPACE =
157 new ParserNamespace<>("prefix-to-xmlnamespace");
160 * Source-specific mapping of prefix strings to module context.
162 // FIXME: the context should expose ModuleStatement
163 public static final @NonNull ParserNamespace<String, StmtContext<?, ?, ?>> IMPORT_PREFIX_TO_MODULECTX =
164 new ParserNamespace<>("import-prefix-to-modulectx");
166 // FIXME: document this
167 public static final @NonNull ParserNamespace<SourceIdentifier, StmtContext<?, ?, ?>> IMPORTED_MODULE =
168 new ParserNamespace<>("imported-module");
170 // FIXME: document this
171 // FIXME: is this 'included submodule' instead?
172 public static final @NonNull ParserNamespace<SourceIdentifier, StmtContext<?, ?, ?>> INCLUDED_MODULE =
173 new ParserNamespace<>("included-module");
176 * Source-specific mapping of prefixes to namespaces.
178 // FIXME: bad javadoc
179 // FIXME: the context should expose SubmoduleStatement
180 public static final @NonNull ParserNamespace<Unqualified, StmtContext<?, ?, ?>> INCLUDED_SUBMODULE_NAME_TO_MODULECTX
181 = new ParserNamespace<>("included-submodule-to-modulectx");
184 * Source-specific mapping of prefixes to namespaces.
186 // FIXME: bad javadoc
187 public static final @NonNull ParserNamespace<Unqualified, QNameModule> MODULE_NAME_TO_QNAME =
188 new ParserNamespace<>("module-name-to-qnamemodule");
191 * Global mapping of modules to QNameModules.
193 public static final @NonNull ParserNamespace<StmtContext<?, ?, ?>, QNameModule> MODULECTX_TO_QNAME =
194 new ParserNamespace<>("modulectx-to-qnamemodule");
196 public static final @NonNull ParserNamespace<Empty, FeatureSet> SUPPORTED_FEATURES =
197 new ParserNamespace<>("supportedFeatures");
200 * Source-specific mapping of prefixes to namespaces. This namespace is populated by all statements which have
201 * impact on the XML namespace, for example {@code import}, {@code belongs-to} and really anywhere a {@code prefix}
202 * statement is present.
204 * @see PrefixResolver
206 public static final @NonNull ParserNamespace<String, QNameModule> PREFIX_TO_MODULE =
207 new ParserNamespace<>("prefix-to-qnamemodule");
210 * Namespace used for storing information about modules that support deviation resolution.
211 * Map key (QNameModule) denotes a module which can be deviated by the modules specified in the Map value.
213 public static final @NonNull ParserNamespace<Empty, SetMultimap<QNameModule, QNameModule>> MODULES_DEVIATED_BY =
214 new ParserNamespace<>("moduleDeviations");
217 * Source-specific mapping of prefixes to namespaces.
219 // FIXME: bad javadoc
220 public static final @NonNull ParserNamespace<QNameModule, Unqualified> MODULE_NAMESPACE_TO_NAME =
221 new ParserNamespace<>("qnamemodule-to-name");
224 * Pre-linkage global mapping of module names to namespaces.
226 public static final @NonNull ParserNamespace<Unqualified, XMLNamespace> MODULE_NAME_TO_NAMESPACE =
227 new ParserNamespace<>("module-name-to-xmlnamespace");
230 * Global mapping of modules to source identifier.
232 public static final @NonNull ParserNamespace<StmtContext<?, ?, ?>, SourceIdentifier> MODULECTX_TO_SOURCE =
233 new ParserNamespace<>("modulectx-to-source");
235 private static final @NonNull ParserNamespace<?, ?> SCHEMA_TREE = new ParserNamespace<>("schemaTree");
238 * Statement local namespace, which holds direct schema node descendants. This corresponds to the contents of the
239 * schema tree as exposed through {@link SchemaTreeAwareEffectiveStatement}.
242 * Unlike all other namespaces this namespaces is polymorphic, hence it is exposed throught this method.
244 * @return Schema tree namespace
246 @SuppressWarnings("unchecked")
247 public static <D extends DeclaredStatement<QName>, E extends SchemaTreeEffectiveStatement<D>>
248 @NonNull ParserNamespace<QName, StmtContext<QName, D, E>> schemaTree() {
249 return (ParserNamespace<QName, StmtContext<QName, D, E>>) SCHEMA_TREE;
252 private ParserNamespaces() {
257 * Find statement context identified by interpreting specified {@link SchemaNodeIdentifier} starting at specified
258 * {@link StmtContext}.
260 * @param root Search root context
261 * @param identifier {@link SchemaNodeIdentifier} relative to search root
262 * @return Matching statement context, if present.
263 * @throws NullPointerException if any of the arguments is null
265 public static Optional<StmtContext<?, ?, ?>> findSchemaTreeStatement(final StmtContext<?, ?, ?> root,
266 final SchemaNodeIdentifier identifier) {
267 final var iterator = identifier.getNodeIdentifiers().iterator();
268 if (!iterator.hasNext()) {
269 return Optional.of(root);
272 QName nextPath = iterator.next();
273 var current = root.namespaceItem(schemaTree(), nextPath);
274 if (current == null) {
275 return Optional.ofNullable(tryToFindUnknownStatement(nextPath.getLocalName(), root));
277 while (current != null && iterator.hasNext()) {
278 nextPath = iterator.next();
279 final var nextNodeCtx = current.namespaceItem(schemaTree(), nextPath);
280 if (nextNodeCtx == null) {
281 return Optional.ofNullable(tryToFindUnknownStatement(nextPath.getLocalName(), current));
283 current = nextNodeCtx;
285 return Optional.ofNullable(current);
288 @SuppressWarnings("unchecked")
289 private static StmtContext<?, ?, ?> tryToFindUnknownStatement(final String localName,
290 final StmtContext<?, ?, ?> current) {
291 final Collection<? extends StmtContext<?, ?, ?>> unknownSubstatements = StmtContextUtils.findAllSubstatements(
292 current, UnknownStatement.class);
293 for (final var unknownSubstatement : unknownSubstatements) {
294 if (localName.equals(unknownSubstatement.rawArgument())) {
295 return unknownSubstatement;