package org.opendaylight.yangtools.rfc8040.model.api;
import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeAwareEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.UnknownEffectiveStatement;
/**
* <a href="https://tools.ietf.org/html/rfc8040#section-8">RFC 8040</a>.
*/
@Beta
-public interface YangDataEffectiveStatement extends UnknownEffectiveStatement<String, YangDataStatement> {
+public interface YangDataEffectiveStatement extends UnknownEffectiveStatement<String, YangDataStatement>,
+ DataTreeAwareEffectiveStatement<String, YangDataStatement> {
@Override
default StatementDefinition statementDefinition() {
return YangDataStatements.YANG_DATA;
*
* @return container statement
*/
- ContainerEffectiveStatement getContainer();
+ @NonNull ContainerEffectiveStatement getContainer();
}
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-xpath-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>rfc8040-model-api</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
exports org.opendaylight.yangtools.yang.model.util;
requires transitive com.google.common;
+ requires transitive org.opendaylight.yangtools.concepts;
+ requires transitive org.opendaylight.yangtools.yang.common;
requires transitive org.opendaylight.yangtools.yang.model.api;
requires transitive org.opendaylight.yangtools.yang.model.spi;
requires transitive org.opendaylight.yangtools.yang.repo.api;
requires transitive org.opendaylight.yangtools.yang.xpath.api;
- requires transitive org.opendaylight.yangtools.yang.common;
- requires transitive org.opendaylight.yangtools.concepts;
+ requires transitive org.opendaylight.yangtools.rfc8040.model.api;
requires org.slf4j;
// Annotations
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.rfc8040.model.api.YangDataEffectiveStatement;
import org.opendaylight.yangtools.yang.common.AbstractQName;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
return pushTypedef(requireNonNull(nodeIdentifier));
}
+ /**
+ * Lookup a {@code rc:yang-data} by the module namespace where it is defined and its template name.
+ *
+ * @param namespace Module namespace in which to lookup the template
+ * @param name Template name
+ * @return Resolved yang-data
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if the corresponding yang-data cannot be found
+ * @throws IllegalStateException if this stack is not empty
+ */
+ public @NonNull YangDataEffectiveStatement enterYangData(final QNameModule namespace, final String name) {
+ final EffectiveStatement<?, ?> parent = deque.peekFirst();
+ checkState(parent == null, "Cannot lookup yang-data in a non-empty stack");
+
+ final String templateName = requireNonNull(name);
+ final ModuleEffectiveStatement module = effectiveModel.getModuleStatements().get(requireNonNull(namespace));
+ checkArgument(module != null, "Module for %s not found", namespace);
+
+ final YangDataEffectiveStatement ret = module.streamEffectiveSubstatements(YangDataEffectiveStatement.class)
+ .filter(stmt -> templateName.equals(stmt.argument()))
+ .findFirst()
+ .orElseThrow(
+ () -> new IllegalArgumentException("yang-data " + templateName + " not present in " + namespace));
+ deque.push(ret);
+ currentModule = module;
+ return ret;
+ }
+
/**
* Pop the current statement from the stack.
*
--- /dev/null
+/*
+ * Copyright (c) 2021 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.model.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.XMLNamespace;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class YT1297Test {
+ private static final QNameModule RESTCONF =
+ QNameModule.create(XMLNamespace.of("urn:ietf:params:xml:ns:yang:ietf-restconf"), Revision.of("2017-01-26"));
+ private static final QNameModule BAD_MODULE =
+ QNameModule.create(XMLNamespace.of("urn:ietf:params:xml:ns:yang:ietf-restconf"), Revision.of("2018-01-26"));
+
+ private static EffectiveModelContext context;
+
+ private final SchemaInferenceStack stack = SchemaInferenceStack.of(context);
+
+ @BeforeClass
+ public static void beforeClass() {
+ context = YangParserTestUtils.parseYangResource("/ietf-restconf.yang");
+ }
+
+ @Test
+ public void testEnterYangData() {
+ assertNotNull(stack.enterYangData(RESTCONF, "yang-api"));
+ assertNotNull(stack.enterDataTree(QName.create(RESTCONF, "restconf")));
+ }
+
+ @Test
+ public void testEnterYangDataNegative() {
+ Exception ex = assertThrows(IllegalArgumentException.class, () -> stack.enterYangData(RESTCONF, "bad-name"));
+ assertEquals("yang-data bad-name not present in " + RESTCONF, ex.getMessage());
+ ex = assertThrows(IllegalArgumentException.class, () -> stack.enterYangData(BAD_MODULE, "whatever"));
+ assertEquals("Module for " + BAD_MODULE + " not found", ex.getMessage());
+
+ assertNotNull(stack.enterGrouping(QName.create(RESTCONF, "errors")));
+ ex = assertThrows(IllegalStateException.class, () -> stack.enterYangData(RESTCONF, "yang-api"));
+ assertEquals("Cannot lookup yang-data in a non-empty stack", ex.getMessage());
+ }
+}
--- /dev/null
+module ietf-restconf {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-restconf";
+ prefix "rc";
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web: <https://datatracker.ietf.org/wg/netconf/>
+ WG List: <mailto:netconf@ietf.org>
+ Author: Andy Bierman
+ <mailto:andy@yumaworks.com>
+ Author: Martin Bjorklund
+ <mailto:mbj@tail-f.com>
+ Author: Kent Watsen
+ <mailto:kwatsen@juniper.net>";
+
+ description
+ "This module contains conceptual YANG specifications
+ for basic RESTCONF media type definitions used in
+ RESTCONF protocol messages.
+ Note that the YANG definitions within this module do not
+ represent configuration data of any kind.
+ The 'restconf-media-type' YANG extension statement
+ provides a normative syntax for XML and JSON
+ message-encoding purposes.
+ Copyright (c) 2017 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+ This version of this YANG module is part of RFC 8040; see
+ the RFC itself for full legal notices.";
+
+ revision 2017-01-26 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 8040: RESTCONF Protocol.";
+ }
+
+ extension yang-data {
+ argument name {
+ yin-element true;
+ }
+ description
+ "This extension is used to specify a YANG data template that
+ represents conceptual data defined in YANG. It is
+ intended to describe hierarchical data independent of
+ protocol context or specific message-encoding format.
+ Data definition statements within a yang-data extension
+ specify the generic syntax for the specific YANG data
+ template, whose name is the argument of the 'yang-data'
+ extension statement.
+ Note that this extension does not define a media type.
+ A specification using this extension MUST specify the
+ message-encoding rules, including the content media type.
+ The mandatory 'name' parameter value identifies the YANG
+ data template that is being defined. It contains the
+ template name.
+ This extension is ignored unless it appears as a top-level
+ statement. It MUST contain data definition statements
+ that result in exactly one container data node definition.
+ An instance of a YANG data template can thus be translated
+ into an XML instance document, whose top-level element
+ corresponds to the top-level container.
+ The module name and namespace values for the YANG module using
+ the extension statement are assigned to instance document data
+ conforming to the data definition statements within
+ this extension.
+ The substatements of this extension MUST follow the
+ 'data-def-stmt' rule in the YANG ABNF.
+ The XPath document root is the extension statement itself,
+ such that the child nodes of the document root are
+ represented by the data-def-stmt substatements within
+ this extension. This conceptual document is the context
+ for the following YANG statements:
+ - must-stmt
+ - when-stmt
+ - path-stmt
+ - min-elements-stmt
+ - max-elements-stmt
+ - mandatory-stmt
+ - unique-stmt
+ - ordered-by
+ - instance-identifier data type
+ The following data-def-stmt substatements are constrained
+ when used within a 'yang-data' extension statement.
+ - The list-stmt is not required to have a key-stmt defined.
+ - The if-feature-stmt is ignored if present.
+ - The config-stmt is ignored if present.
+ - The available identity values for any 'identityref'
+ leaf or leaf-list nodes are limited to the module
+ containing this extension statement and the modules
+ imported into that module.
+ ";
+ }
+
+ rc:yang-data yang-errors {
+ uses errors;
+ }
+
+ rc:yang-data yang-api {
+ uses restconf;
+ }
+
+ grouping errors {
+ description
+ "A grouping that contains a YANG container
+ representing the syntax and semantics of a
+ YANG Patch error report within a response message.";
+
+ container errors {
+ description
+ "Represents an error report returned by the server if
+ a request results in an error.";
+
+ list error {
+ description
+ "An entry containing information about one
+ specific error that occurred while processing
+ a RESTCONF request.";
+ reference
+ "RFC 6241, Section 4.3.";
+
+ leaf error-type {
+ type enumeration {
+ enum transport {
+ description
+ "The transport layer.";
+ }
+ enum rpc {
+ description
+ "The rpc or notification layer.";
+ }
+ enum protocol {
+ description
+ "The protocol operation layer.";
+ }
+ enum application {
+ description
+ "The server application layer.";
+ }
+ }
+ mandatory true;
+ description
+ "The protocol layer where the error occurred.";
+ }
+
+ leaf error-tag {
+ type string;
+ mandatory true;
+ description
+ "The enumerated error-tag.";
+ }
+
+ leaf error-app-tag {
+ type string;
+ description
+ "The application-specific error-tag.";
+ }
+
+ leaf error-path {
+ type instance-identifier;
+ description
+ "The YANG instance identifier associated
+ with the error node.";
+ }
+
+ leaf error-message {
+ type string;
+ description
+ "A message describing the error.";
+ }
+
+ anydata error-info {
+ description
+ "This anydata value MUST represent a container with
+ zero or more data nodes representing additional
+ error information.";
+ }
+ }
+ }
+ }
+
+ grouping restconf {
+ description
+ "Conceptual grouping representing the RESTCONF
+ root resource.";
+
+ container restconf {
+ description
+ "Conceptual container representing the RESTCONF
+ root resource.";
+
+ container data {
+ description
+ "Container representing the datastore resource.
+ Represents the conceptual root of all state data
+ and configuration data supported by the server.
+ The child nodes of this container can be any data
+ resources that are defined as top-level data nodes
+ from the YANG modules advertised by the server in
+ the 'ietf-yang-library' module.";
+ }
+
+ container operations {
+ description
+ "Container for all operation resources.
+ Each resource is represented as an empty leaf with the
+ name of the RPC operation from the YANG 'rpc' statement.
+ For example, the 'system-restart' RPC operation defined
+ in the 'ietf-system' module would be represented as
+ an empty leaf in the 'ietf-system' namespace. This is
+ a conceptual leaf and will not actually be found in
+ the module:
+ module ietf-system {
+ leaf system-reset {
+ type empty;
+ }
+ }
+ To invoke the 'system-restart' RPC operation:
+ POST /restconf/operations/ietf-system:system-restart
+ To discover the RPC operations supported by the server:
+ GET /restconf/operations
+ In XML, the YANG module namespace identifies the module:
+ <system-restart
+ xmlns='urn:ietf:params:xml:ns:yang:ietf-system'/>
+ In JSON, the YANG module name identifies the module:
+ { 'ietf-system:system-restart' : [null] }
+ ";
+ }
+
+ leaf yang-library-version {
+ type string {
+ pattern '\d{4}-\d{2}-\d{2}';
+ }
+ config false;
+ mandatory true;
+ description
+ "Identifies the revision date of the 'ietf-yang-library'
+ module that is implemented by this RESTCONF server.
+ Indicates the year, month, and day in YYYY-MM-DD
+ numeric format.";
+ }
+ }
+ }
+
+}
\ No newline at end of file
import org.opendaylight.yangtools.yang.parser.spi.meta.ParserNamespace;
/**
- * Namespace for remembering the {@code yang-data} argument's QName. This namespace is necessary because we are forced
- * to have a String argument due to us being an extension.
+ * Namespace for remembering the {@code yang-data} argument's QName.
*/
@Beta
-// FIXME: YANGTOOLS-1196: remove this namespace once we can bind freely
+// FIXME: We should not be needing this namespace, as yang-data's argument is not documented anywhere to be compatible
+// with 'identifier', hence we cannot safely form a QName.
public interface YangDataArgumentNamespace extends ParserNamespace<Empty, QName> {
NamespaceBehaviour<Empty, QName, @NonNull YangDataArgumentNamespace> BEHAVIOUR =
NamespaceBehaviour.statementLocal(YangDataArgumentNamespace.class);
*/
package org.opendaylight.yangtools.rfc8040.parser;
+import static com.google.common.base.Verify.verify;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
-import com.google.common.base.Verify;
import com.google.common.collect.ImmutableList;
+import java.util.Map;
+import java.util.Optional;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.rfc8040.model.api.YangDataEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.SchemaNodeDefaults;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeAwareEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.UnknownEffectiveStatementBase;
import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current;
import org.opendaylight.yangtools.yang.parser.spi.meta.SchemaPathSupport;
@Beta
final class YangDataEffectiveStatementImpl extends UnknownEffectiveStatementBase<String, YangDataStatement>
implements YangDataEffectiveStatement, YangDataSchemaNode {
-
private final @Nullable SchemaPath path;
private final @NonNull QName argumentQName;
private final @NonNull ContainerEffectiveStatement container;
// TODO: this is strong binding of two API contracts. Unfortunately ContainerEffectiveStatement design is
// incomplete.
- Verify.verify(container instanceof ContainerSchemaNode);
+ verify(container instanceof ContainerSchemaNode, "Incompatible container %s", container);
}
@Override
return this;
}
+ @Override
+ protected <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
+ final Class<N> namespace) {
+ if (SchemaTreeAwareEffectiveStatement.Namespace.class.equals(namespace)
+ || DataTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
+ @SuppressWarnings("unchecked")
+ final Map<K, V> ns = (Map<K, V>)Map.of(container.argument(), container);
+ return Optional.of(ns);
+ }
+ return super.getNamespaceContents(namespace);
+ }
+
@Override
public String toString() {
return MoreObjects.toStringHelper(this).omitNullValues()