Promote SchemaSourceRepresentation
[yangtools.git] / parser / yang-parser-spi / src / main / java / org / opendaylight / yangtools / yang / parser / spi / ParserNamespaces.java
1 /*
2  * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.spi;
9
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;
44
45 /**
46  * Baseline {@link ParserNamespace}s mostly derived from YANG specification.
47  */
48 public final class ParserNamespaces {
49     /**
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.
53      */
54     public static final @NonNull ParserNamespace<QName,
55         StmtContext<QName, ExtensionStatement, ExtensionEffectiveStatement>> EXTENSION =
56         new ParserNamespace<>("extension");
57
58     /**
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
61      * name.
62      */
63     public static final @NonNull ParserNamespace<QName,
64         StmtContext<QName, FeatureStatement, FeatureEffectiveStatement>> FEATURE = new ParserNamespace<>("feature");
65
66     /**
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.
70      *
71      * <p>
72      * This means that any descendant node may use that grouping, and it MUST NOT define a grouping with the same name.
73      */
74     public static final @NonNull ParserNamespace<QName,
75         StmtContext<QName, GroupingStatement, GroupingEffectiveStatement>> GROUPING = new ParserNamespace<>("grouping");
76
77     /**
78      * Identity namespace. All identity names defined in a module and its submodules share the same identity identifier
79      * namespace.
80      */
81     public static final @NonNull ParserNamespace<QName,
82         StmtContext<QName, IdentityStatement, IdentityEffectiveStatement>> IDENTITY = new ParserNamespace<>("identity");
83
84     /**
85      * Module namespace. All modules known to the reactor are populated to this namespace. Each module is identified
86      * by a {@link SourceIdentifier}.
87      */
88     public static final @NonNull ParserNamespace<SourceIdentifier,
89         StmtContext<Unqualified, ModuleStatement, ModuleEffectiveStatement>> MODULE = new ParserNamespace<>("module");
90
91     /**
92      * Submodule equivalent of {@link #MODULE}.
93      */
94     public static final @NonNull ParserNamespace<SourceIdentifier,
95         StmtContext<Unqualified, SubmoduleStatement, SubmoduleEffectiveStatement>> SUBMODULE =
96         new ParserNamespace<>("submodule");
97
98     /**
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.
101      *
102      * <p>
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.
105      *
106      * <p>
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).
109      */
110     public static final @NonNull ParserNamespace<QName,
111         StmtContext<QName, TypedefStatement, TypedefEffectiveStatement>> TYPE = new ParserNamespace<>("typedef");
112
113     /**
114      * A derived namespace allowing lookup of modules based on their {@link QNameModule}.
115      */
116     public static final @NonNull ParserNamespace<QNameModule,
117         StmtContext<Unqualified, ModuleStatement, ModuleEffectiveStatement>> NAMESPACE_TO_MODULE =
118         new ParserNamespace<>("namespace-to-module");
119
120     /**
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.
123      */
124     public static final @NonNull ParserNamespace<Unqualified,
125         StmtContext<Unqualified, ModuleStatement, ModuleEffectiveStatement>> PRELINKAGE_MODULE =
126         new ParserNamespace<>("prelinkage-module");
127
128     /**
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
131      * statement.
132      */
133     public static final @NonNull ParserNamespace<String, StmtContext<?, ?, ?>> BELONGSTO_PREFIX_TO_MODULECTX =
134         new ParserNamespace<>("belongsto-prefix-to-module");
135
136     /**
137      * Source-specific mapping of prefixes to namespaces.
138      */
139     // FIXME: bad javadoc
140     public static final @NonNull ParserNamespace<String, Unqualified> BELONGSTO_PREFIX_TO_MODULE_NAME =
141         new ParserNamespace<>("belongsto-prefix-to-name");
142
143     /**
144      * Namespace similar to {@link ParserNamespaces#MODULE} for storing modules into Yang model storage but keyed by
145      * plain name.
146      */
147     // FIXME: Better name?
148     public static final @NonNull ParserNamespace<Unqualified,
149         StmtContext<Unqualified, ModuleStatement, ModuleEffectiveStatement>> MODULE_FOR_BELONGSTO =
150         new ParserNamespace<>("module-belongsto");
151
152     /**
153      * Pre-linkage source-specific mapping of prefixes to module namespaces.
154      */
155     // FIXME: a better name?
156     public static final @NonNull ParserNamespace<String, XMLNamespace> IMP_PREFIX_TO_NAMESPACE =
157         new ParserNamespace<>("prefix-to-xmlnamespace");
158
159     /**
160      * Source-specific mapping of prefix strings to module context.
161      */
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");
165
166     // FIXME: document this
167     public static final @NonNull ParserNamespace<SourceIdentifier, StmtContext<?, ?, ?>> IMPORTED_MODULE =
168         new ParserNamespace<>("imported-module");
169
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");
174
175     /**
176      * Source-specific mapping of prefixes to namespaces.
177      */
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");
182
183     /**
184      * Source-specific mapping of prefixes to namespaces.
185      */
186     // FIXME: bad javadoc
187     public static final @NonNull ParserNamespace<Unqualified, QNameModule> MODULE_NAME_TO_QNAME =
188         new ParserNamespace<>("module-name-to-qnamemodule");
189
190     /**
191      * Global mapping of modules to QNameModules.
192      */
193     public static final @NonNull ParserNamespace<StmtContext<?, ?, ?>, QNameModule> MODULECTX_TO_QNAME =
194         new ParserNamespace<>("modulectx-to-qnamemodule");
195
196     public static final @NonNull ParserNamespace<Empty, FeatureSet> SUPPORTED_FEATURES =
197         new ParserNamespace<>("supportedFeatures");
198
199     /**
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.
203      *
204      * @see PrefixResolver
205      */
206     public static final @NonNull ParserNamespace<String, QNameModule> PREFIX_TO_MODULE =
207         new ParserNamespace<>("prefix-to-qnamemodule");
208
209     /**
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.
212      */
213     public static final @NonNull ParserNamespace<Empty, SetMultimap<QNameModule, QNameModule>> MODULES_DEVIATED_BY =
214         new ParserNamespace<>("moduleDeviations");
215
216     /**
217      * Source-specific mapping of prefixes to namespaces.
218      */
219     // FIXME: bad javadoc
220     public static final @NonNull ParserNamespace<QNameModule, Unqualified> MODULE_NAMESPACE_TO_NAME =
221         new ParserNamespace<>("qnamemodule-to-name");
222
223     /**
224      * Pre-linkage global mapping of module names to namespaces.
225      */
226     public static final @NonNull ParserNamespace<Unqualified, XMLNamespace> MODULE_NAME_TO_NAMESPACE =
227         new ParserNamespace<>("module-name-to-xmlnamespace");
228
229     /**
230      * Global mapping of modules to source identifier.
231      */
232     public static final @NonNull ParserNamespace<StmtContext<?, ?, ?>, SourceIdentifier> MODULECTX_TO_SOURCE =
233         new ParserNamespace<>("modulectx-to-source");
234
235     private static final @NonNull ParserNamespace<?, ?> SCHEMA_TREE = new ParserNamespace<>("schemaTree");
236
237     /**
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}.
240      *
241      * <p>
242      * Unlike all other namespaces this namespaces is polymorphic, hence it is exposed throught this method.
243      *
244      * @return Schema tree namespace
245      */
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;
250     }
251
252     private ParserNamespaces() {
253         // Hidden on purpose
254     }
255
256     /**
257      * Find statement context identified by interpreting specified {@link SchemaNodeIdentifier} starting at specified
258      * {@link StmtContext}.
259      *
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
264      */
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);
270         }
271
272         QName nextPath = iterator.next();
273         var current = root.namespaceItem(schemaTree(), nextPath);
274         if (current == null) {
275             return Optional.ofNullable(tryToFindUnknownStatement(nextPath.getLocalName(), root));
276         }
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));
282             }
283             current = nextNodeCtx;
284         }
285         return Optional.ofNullable(current);
286     }
287
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;
296             }
297         }
298         return null;
299     }
300 }