org.opendaylight.yangtools.yang.parser.spi.ParserNamespaces Maven / Gradle / Ivy
/*
* Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.yangtools.yang.parser.spi;
import com.google.common.collect.SetMultimap;
import java.util.Collection;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.yang.common.Empty;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.common.UnresolvedQName.Unqualified;
import org.opendaylight.yangtools.yang.common.XMLNamespace;
import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.FeatureEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.model.api.stmt.FeatureStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.GroupingEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.GroupingStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.IdentityEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.IdentityStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.TypedefStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
import org.opendaylight.yangtools.yang.parser.spi.meta.ParserNamespace;
import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
import org.opendaylight.yangtools.yang.parser.spi.source.PrefixResolver;
/**
* Baseline {@link ParserNamespace}s mostly derived from YANG specification.
*/
public final class ParserNamespaces {
/**
* Extension namespace. All extension names defined in a module and its submodules share the same extension
* identifier namespace, where each extension is identified by a QName formed from the defining module's QNameModule
* and the identifier specified in extension statement's argument.
*/
public static final @NonNull ParserNamespace> EXTENSION =
new ParserNamespace<>("extension");
/**
* Feature namespace. All feature names defined in a module and its submodules share the same feature identifier
* namespace. Each feature is identified by a QName formed from the defining module's QNameModule and the feature
* name.
*/
public static final @NonNull ParserNamespace> FEATURE = new ParserNamespace<>("feature");
/**
* Grouping namespace. * All grouping names defined within a parent node or at the top level of the module
* or its submodules share the same grouping identifier namespace. This namespace is scoped to all
* descendant nodes of the parent node or module.
*
* This means that any descendant node may use that grouping, and it MUST NOT define a grouping with the same
* name.
*/
public static final @NonNull ParserNamespace> GROUPING = new ParserNamespace<>("grouping");
/**
* Identity namespace. All identity names defined in a module and its submodules share the same identity identifier
* namespace.
*/
public static final @NonNull ParserNamespace> IDENTITY = new ParserNamespace<>("identity");
/**
* Module namespace. All modules known to the reactor are populated to this namespace. Each module is identified
* by a {@link SourceIdentifier}.
*/
public static final @NonNull ParserNamespace> MODULE = new ParserNamespace<>("module");
/**
* Submodule equivalent of {@link #MODULE}.
*/
public static final @NonNull ParserNamespace> SUBMODULE =
new ParserNamespace<>("submodule");
/**
* Derived types namespace. All derived type names defined within a parent node or at the top level of the module
* (or its submodules) share the same type identifier namespace.
*
* This namespace is scoped to all descendant nodes of the parent node or module. This means that any descendant
* node may use that typedef, and it MUST NOT define a typedef with the same name.
*
*
This namespace includes all type definitions implied by the language in which the current statement resides
* (e.g. RFC6020/RFC7950 for YANG 1.0/1.1).
*/
public static final @NonNull ParserNamespace> TYPE = new ParserNamespace<>("typedef");
/**
* A derived namespace allowing lookup of modules based on their {@link QNameModule}.
*/
public static final @NonNull ParserNamespace> NAMESPACE_TO_MODULE =
new ParserNamespace<>("namespace-to-module");
/**
* Intermediate-stage namespace equivalent to {@link #MODULE} except it is keyed by module names. This namespace is
* used to resolve inter-module references before actual linkage occurs.
*/
public static final @NonNull ParserNamespace> PRELINKAGE_MODULE =
new ParserNamespace<>("prelinkage-module");
/**
* Source-specific mapping of belongsTo prefixes to module identifiers. This mapping allows source-specific context
* to correctly populate prefixes map for actual parsing phase and eventually, resolve QName for any valid declared
* statement.
*/
public static final @NonNull ParserNamespace> BELONGSTO_PREFIX_TO_MODULECTX =
new ParserNamespace<>("belongsto-prefix-to-module");
/**
* Source-specific mapping of prefixes to namespaces.
*/
// FIXME: bad javadoc
public static final @NonNull ParserNamespace BELONGSTO_PREFIX_TO_MODULE_NAME =
new ParserNamespace<>("belongsto-prefix-to-name");
/**
* Namespace similar to {@link ParserNamespaces#MODULE} for storing modules into Yang model storage but keyed by
* plain name.
*/
// FIXME: Better name?
public static final @NonNull ParserNamespace> MODULE_FOR_BELONGSTO =
new ParserNamespace<>("module-belongsto");
/**
* Pre-linkage source-specific mapping of prefixes to module namespaces.
*/
// FIXME: a better name?
public static final @NonNull ParserNamespace IMP_PREFIX_TO_NAMESPACE =
new ParserNamespace<>("prefix-to-xmlnamespace");
/**
* Source-specific mapping of prefix strings to module context.
*/
// FIXME: the context should expose ModuleStatement
public static final @NonNull ParserNamespace> IMPORT_PREFIX_TO_MODULECTX =
new ParserNamespace<>("import-prefix-to-modulectx");
// FIXME: document this
public static final @NonNull ParserNamespace> IMPORTED_MODULE =
new ParserNamespace<>("imported-module");
// FIXME: document this
// FIXME: is this 'included submodule' instead?
public static final @NonNull ParserNamespace> INCLUDED_MODULE =
new ParserNamespace<>("included-module");
/**
* Source-specific mapping of prefixes to namespaces.
*/
// FIXME: bad javadoc
// FIXME: the context should expose SubmoduleStatement
public static final @NonNull ParserNamespace> INCLUDED_SUBMODULE_NAME_TO_MODULECTX
= new ParserNamespace<>("included-submodule-to-modulectx");
/**
* Source-specific mapping of prefixes to namespaces.
*/
// FIXME: bad javadoc
public static final @NonNull ParserNamespace MODULE_NAME_TO_QNAME =
new ParserNamespace<>("module-name-to-qnamemodule");
/**
* Global mapping of modules to QNameModules.
*/
public static final @NonNull ParserNamespace, QNameModule> MODULECTX_TO_QNAME =
new ParserNamespace<>("modulectx-to-qnamemodule");
public static final @NonNull ParserNamespace SUPPORTED_FEATURES =
new ParserNamespace<>("supportedFeatures");
/**
* Source-specific mapping of prefixes to namespaces. This namespace is populated by all statements which have
* impact on the XML namespace, for example {@code import}, {@code belongs-to} and really anywhere a {@code prefix}
* statement is present.
*
* @see PrefixResolver
*/
public static final @NonNull ParserNamespace PREFIX_TO_MODULE =
new ParserNamespace<>("prefix-to-qnamemodule");
/**
* Namespace used for storing information about modules that support deviation resolution.
* Map key (QNameModule) denotes a module which can be deviated by the modules specified in the Map value.
*/
public static final @NonNull ParserNamespace> MODULES_DEVIATED_BY =
new ParserNamespace<>("moduleDeviations");
/**
* Source-specific mapping of prefixes to namespaces.
*/
// FIXME: bad javadoc
public static final @NonNull ParserNamespace MODULE_NAMESPACE_TO_NAME =
new ParserNamespace<>("qnamemodule-to-name");
/**
* Pre-linkage global mapping of module names to namespaces.
*/
public static final @NonNull ParserNamespace MODULE_NAME_TO_NAMESPACE =
new ParserNamespace<>("module-name-to-xmlnamespace");
/**
* Global mapping of modules to source identifier.
*/
public static final @NonNull ParserNamespace, SourceIdentifier> MODULECTX_TO_SOURCE =
new ParserNamespace<>("modulectx-to-source");
private static final @NonNull ParserNamespace, ?> SCHEMA_TREE = new ParserNamespace<>("schemaTree");
/**
* Statement local namespace, which holds direct schema node descendants. This corresponds to the contents of the
* schema tree as exposed through {@link SchemaTreeAwareEffectiveStatement}.
*
* Unlike all other namespaces this namespaces is polymorphic, hence it is exposed throught this method.
*
* @return Schema tree namespace
*/
@SuppressWarnings("unchecked")
public static , E extends SchemaTreeEffectiveStatement>
@NonNull ParserNamespace> schemaTree() {
return (ParserNamespace>) SCHEMA_TREE;
}
private ParserNamespaces() {
// Hidden on purpose
}
/**
* Find statement context identified by interpreting specified {@link SchemaNodeIdentifier} starting at specified
* {@link StmtContext}.
*
* @param root Search root context
* @param identifier {@link SchemaNodeIdentifier} relative to search root
* @return Matching statement context, if present.
* @throws NullPointerException if any of the arguments is null
*/
public static Optional> findSchemaTreeStatement(final StmtContext, ?, ?> root,
final SchemaNodeIdentifier identifier) {
final var iterator = identifier.getNodeIdentifiers().iterator();
if (!iterator.hasNext()) {
return Optional.of(root);
}
QName nextPath = iterator.next();
var current = root.namespaceItem(schemaTree(), nextPath);
if (current == null) {
return Optional.ofNullable(tryToFindUnknownStatement(nextPath.getLocalName(), root));
}
while (current != null && iterator.hasNext()) {
nextPath = iterator.next();
final var nextNodeCtx = current.namespaceItem(schemaTree(), nextPath);
if (nextNodeCtx == null) {
return Optional.ofNullable(tryToFindUnknownStatement(nextPath.getLocalName(), current));
}
current = nextNodeCtx;
}
return Optional.ofNullable(current);
}
@SuppressWarnings("unchecked")
private static StmtContext, ?, ?> tryToFindUnknownStatement(final String localName,
final StmtContext, ?, ?> current) {
final Collection extends StmtContext, ?, ?>> unknownSubstatements = StmtContextUtils.findAllSubstatements(
current, UnknownStatement.class);
for (final var unknownSubstatement : unknownSubstatements) {
if (localName.equals(unknownSubstatement.rawArgument())) {
return unknownSubstatement;
}
}
return null;
}
}