<groupId>org.opendaylight.yangtools</groupId>
<artifactId>odlext-model-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>rfc8040-model-api</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-model-spi</artifactId>
requires org.opendaylight.yangtools.yang.model.ri;
requires org.opendaylight.yangtools.yang.model.util;
requires org.opendaylight.yangtools.odlext.model.api;
+ requires org.opendaylight.yangtools.rfc8040.model.api;
requires org.slf4j;
// Annotations
import org.opendaylight.mdsal.binding.model.ri.BindingTypes;
import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
+import org.opendaylight.yangtools.rfc8040.model.api.YangDataEffectiveStatement;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware;
import org.opendaylight.yangtools.yang.model.api.CopyableNode;
tmpAug.add(new UsesAugmentGenerator(usesAug, uses, this));
}
}
+ } else if (stmt instanceof YangDataEffectiveStatement yangData) {
+ if (this instanceof ModuleGenerator moduleGen) {
+ tmp.add(YangDataGenerator.of(yangData, moduleGen));
+ }
} else {
LOG.trace("Ignoring statement {}", stmt);
}
Map.entry(yangModuleInfo, localName.getLocalName()));
}
+ // FIXME: use YangDataName
+ void addNameConstant(final GeneratedTypeBuilderBase<?> builder, final String templateName) {
+ builder.addConstant(BindingTypes.YANG_DATA_NAME, BindingMapping.NAME_STATIC_FIELD_NAME,
+ Map.entry(yangModuleInfo, templateName));
+ }
+
@Override
CompositeRuntimeTypeBuilder<ModuleEffectiveStatement, ModuleRuntimeType> createBuilder(
final ModuleEffectiveStatement statement) {
* The namespace of all {@code grouping} statements, bullet 6.
*/
GROUPING("$G"),
+ /**
+ * The namespace of all RFC8040 {@code ietf-restconf:yang-data} statements. These sit outside of the usual YANG
+ * statement namespaces, but may overlap in the usual case when template names conform to YANG {@code identifier}
+ * rules.
+ */
+ YANG_DATA("$YD"),
/**
* All other processed statements. Includes {@code augment}, and {@code schema tree} statements.
*/
--- /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.mdsal.binding.generator.impl.reactor;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.generator.impl.reactor.CollisionDomain.Member;
+import org.opendaylight.mdsal.binding.generator.impl.rt.DefaultYangDataRuntimeType;
+import org.opendaylight.mdsal.binding.model.api.GeneratedType;
+import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
+import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
+import org.opendaylight.mdsal.binding.model.ri.BindingTypes;
+import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType;
+import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
+import org.opendaylight.mdsal.binding.runtime.api.YangDataRuntimeType;
+import org.opendaylight.yangtools.rfc8040.model.api.YangDataEffectiveStatement;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.UnresolvedQName;
+import org.opendaylight.yangtools.yang.common.UnresolvedQName.Unqualified;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
+
+/**
+ * Generator corresponding to a {@code rc:yang-data} statement.
+ */
+abstract sealed class YangDataGenerator
+ extends AbstractCompositeGenerator<YangDataEffectiveStatement, YangDataRuntimeType> {
+ private static final class WithIdentifier extends YangDataGenerator {
+ private final @NonNull Unqualified identifier;
+
+ WithIdentifier(final YangDataEffectiveStatement statement, final ModuleGenerator parent,
+ final Unqualified identifier) {
+ super(statement, parent);
+ this.identifier = requireNonNull(identifier);
+ }
+
+ @Override
+ CamelCaseNamingStrategy createNamingStrategy() {
+ return new CamelCaseNamingStrategy(namespace(), identifier);
+ }
+ }
+
+ private static final class WithString extends YangDataGenerator {
+ WithString(final YangDataEffectiveStatement statement, final ModuleGenerator parent) {
+ super(statement, parent);
+ }
+
+ @Override
+ YangDataNamingStrategy createNamingStrategy() {
+ return new YangDataNamingStrategy(statement().argument());
+ }
+ }
+
+ private YangDataGenerator(final YangDataEffectiveStatement statement, final ModuleGenerator parent) {
+ super(statement, parent);
+ }
+
+ static @NonNull YangDataGenerator of(final YangDataEffectiveStatement statement, final ModuleGenerator parent) {
+ // yang-data's argument is not guaranteed to comply with YANG 'identifier', but it usually does. If it does, we
+ // use the usual mechanics, but if it does not, we have to deal with any old string, similar to what we do for
+ // bit names. Here we decide which path to take.
+ final String templateName = statement.argument();
+ final var identifier = UnresolvedQName.tryLocalName(templateName);
+ return identifier != null ? new WithIdentifier(statement, parent, identifier)
+ : new WithString(statement, parent);
+ }
+
+ @Override
+ final void pushToInference(final SchemaInferenceStack dataTree) {
+ final QNameModule moduleQName = currentModule().getQName().getModule();
+ dataTree.enterYangData(moduleQName, statement().argument());
+ }
+
+ @Override
+ final ClassPlacement classPlacement() {
+ return ClassPlacement.TOP_LEVEL;
+ }
+
+ @Override
+ final Member createMember(final CollisionDomain domain) {
+ return domain.addPrimary(this, createNamingStrategy());
+ }
+
+ abstract @NonNull ClassNamingStrategy createNamingStrategy();
+
+ @Override
+ final StatementNamespace namespace() {
+ return StatementNamespace.YANG_DATA;
+ }
+
+ @Override
+ final GeneratedType createTypeImpl(final TypeBuilderFactory builderFactory) {
+ final GeneratedTypeBuilder builder = builderFactory.newGeneratedTypeBuilder(typeName());
+
+ builder.addImplementsType(BindingTypes.yangData(builder));
+ addUsesInterfaces(builder, builderFactory);
+ addConcreteInterfaceMethods(builder);
+
+ addGetterMethods(builder, builderFactory);
+
+ final var module = currentModule();
+ module.addNameConstant(builder, statement().argument());
+
+ builder.setModuleName(module.statement().argument().getLocalName());
+ builderFactory.addCodegenInformation(module, statement(), builder);
+
+ return builder.build();
+ }
+
+ @Override
+ final CompositeRuntimeTypeBuilder<YangDataEffectiveStatement, YangDataRuntimeType> createBuilder(
+ final YangDataEffectiveStatement statement) {
+ return new CompositeRuntimeTypeBuilder<>(statement) {
+ @Override
+ YangDataRuntimeType build(final GeneratedType type, final YangDataEffectiveStatement statement,
+ final List<RuntimeType> children, final List<AugmentRuntimeType> augments) {
+ return new DefaultYangDataRuntimeType(type, statement, children);
+ }
+ };
+ }
+
+ @Override
+ final void addAsGetterMethod(final GeneratedTypeBuilderBase<?> builder, final TypeBuilderFactory builderFactory) {
+ // is not a part of any structure
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 PANTHEON.tech s.r.o. 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.mdsal.binding.generator.impl.reactor;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
+
+/**
+ * Naming strategy for {@code ietf-restconf:yang-data} template which has a generic string not matching YANG identifier.
+ */
+@NonNullByDefault
+final class YangDataNamingStrategy extends ClassNamingStrategy {
+ private final String javaIdentifier;
+
+ YangDataNamingStrategy(final String templateName) {
+ javaIdentifier = BindingMapping.mapYangDataName(templateName);
+ }
+
+ @Override
+ String simpleClassName() {
+ return javaIdentifier;
+ }
+
+ @Override
+ @Nullable ClassNamingStrategy fallback() {
+ // javaIdentifier is guaranteed to be unique, there is no need for fallback
+ return null;
+ }
+
+ @Override
+ String rootName() {
+ return javaIdentifier;
+ }
+
+ @Override
+ String childPackage() {
+ // javaIdentifier is always unique and provides an identifier which is suitable for package naming as well,
+ // except we need to further expand the package name so it does not class with the class.
+ // Since the strategy escapes '$', appending one cannot clash.
+ return javaIdentifier + '$';
+ }
+
+ @Override
+ ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+ return helper.add("javaIdentifier", javaIdentifier);
+ }
+}
--- /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.mdsal.binding.generator.impl.rt;
+
+import com.google.common.annotations.Beta;
+import java.util.List;
+import org.opendaylight.mdsal.binding.model.api.GeneratedType;
+import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
+import org.opendaylight.mdsal.binding.runtime.api.YangDataRuntimeType;
+import org.opendaylight.yangtools.rfc8040.model.api.YangDataEffectiveStatement;
+
+@Beta
+public final class DefaultYangDataRuntimeType extends AbstractCompositeRuntimeType<YangDataEffectiveStatement>
+ implements YangDataRuntimeType {
+ public DefaultYangDataRuntimeType(final GeneratedType bindingType, final YangDataEffectiveStatement statement,
+ final List<RuntimeType> children) {
+ super(bindingType, statement, children);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech s.r.o. 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.mdsal.binding.generator.impl;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.in;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.model.api.GeneratedType;
+import org.opendaylight.mdsal.binding.model.api.MethodSignature;
+import org.opendaylight.mdsal.binding.model.api.Type;
+import org.opendaylight.mdsal.binding.model.ri.BaseYangTypes;
+import org.opendaylight.mdsal.binding.model.ri.BindingTypes;
+import org.opendaylight.mdsal.binding.model.ri.Types;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Mdsal675Test {
+ private static final String PACKAGE = "org.opendaylight.yang.gen.v1.urn.test.yang.data.demo.norev.";
+ private static final String PACKAGE2 = "org.opendaylight.yang.gen.v1.urn.test.yang.data.naming.norev.";
+ private static final String MODULE_CLASS_NAME = PACKAGE + "YangDataDemoData";
+ private static final String ROOT_CONTAINER_CLASS_NAME = PACKAGE + "RootContainer";
+ private static final Map<String, Type> INTERFACE_METHODS =
+ Map.of("implementedInterface", Types.typeForClass(Class.class));
+
+ @Test
+ public void yangDataGen() {
+ final List<GeneratedType> allGenTypes = DefaultBindingGenerator.generateFor(
+ YangParserTestUtils.parseYangResources(Mdsal675Test.class,
+ "/yang-data-models/ietf-restconf.yang", "/yang-data-models/yang-data-demo.yang"));
+ assertNotNull(allGenTypes);
+ assertEquals(29, allGenTypes.size());
+ final Map<String, GeneratedType> genTypesMap = allGenTypes.stream()
+ .collect(ImmutableMap.toImmutableMap(type -> type.getIdentifier().toString(), type -> type));
+
+ // ensure generated yang-data classes contain getters for inner structure types
+
+ // yang-data > container
+ assertYangDataGenType(
+ assertGenType(genTypesMap, PACKAGE + "YangDataWithContainer"),
+ assertGenType(genTypesMap, PACKAGE + "yang.data.with.container.ContainerFromYangData"),
+ List.of("getContainerFromYangData", "nonnullContainerFromYangData"));
+ // yang-data > list
+ assertYangDataGenType(
+ assertGenType(genTypesMap, PACKAGE + "YangDataWithList"),
+ Types.listTypeFor(assertGenType(genTypesMap, PACKAGE + "yang.data.with.list.ListFromYangData")),
+ List.of("getListFromYangData", "nonnullListFromYangData"));
+ // yang-data > leaf
+ assertYangDataGenType(
+ assertGenType(genTypesMap, PACKAGE + "YangDataWithLeaf"),
+ BaseYangTypes.STRING_TYPE,
+ List.of("getLeafFromYangData", "requireLeafFromYangData"));
+ // yang-data > leaf-list
+ assertYangDataGenType(
+ assertGenType(genTypesMap, PACKAGE + "YangDataWithLeafList"),
+ Types.setTypeFor(BaseYangTypes.STRING_TYPE),
+ List.of("getLeafListFromYangData", "requireLeafListFromYangData"));
+ // yang-data > anydata
+ assertYangDataGenType(
+ assertGenType(genTypesMap, PACKAGE + "YangDataWithAnydata"),
+ assertGenType(genTypesMap, PACKAGE + "yang.data.with.anydata.AnydataFromYangData"),
+ List.of("getAnydataFromYangData", "requireAnydataFromYangData"));
+ // yang-data > anyxml
+ assertYangDataGenType(
+ assertGenType(genTypesMap, PACKAGE + "YangDataWithAnyxml"),
+ assertGenType(genTypesMap, PACKAGE + "yang.data.with.anyxml.AnyxmlFromYangData"),
+ List.of("getAnyxmlFromYangData", "requireAnyxmlFromYangData"));
+
+ // ensure generated yang-data classes extending inner group so group content is reachable
+
+ // yang-data > uses > group > container
+ assertYangDataGenType(
+ assertGenType(genTypesMap, PACKAGE + "YangDataWithContainerFromGroup"),
+ assertGenType(genTypesMap, PACKAGE + "GrpForContainer"),
+ assertGenType(genTypesMap, PACKAGE + "grp._for.container.ContainerFromGroup"),
+ List.of("getContainerFromGroup", "nonnullContainerFromGroup"));
+ // yang-data > uses > group > list
+ assertYangDataGenType(
+ assertGenType(genTypesMap, PACKAGE + "YangDataWithListFromGroup"),
+ assertGenType(genTypesMap, PACKAGE + "GrpForList"),
+ Types.listTypeFor(assertGenType(genTypesMap, PACKAGE + "grp._for.list.ListFromGroup")),
+ List.of("getListFromGroup", "nonnullListFromGroup")
+ );
+ // yang-data > uses > group > leaf
+ assertYangDataGenType(
+ assertGenType(genTypesMap, PACKAGE + "YangDataWithLeafFromGroup"),
+ assertGenType(genTypesMap, PACKAGE + "GrpForLeaf"),
+ BaseYangTypes.UINT32_TYPE,
+ List.of("getLeafFromGroup", "requireLeafFromGroup"));
+ // yang-data > uses > group > leaf-list
+ assertYangDataGenType(
+ assertGenType(genTypesMap, PACKAGE + "YangDataWithLeafListFromGroup"),
+ assertGenType(genTypesMap, PACKAGE + "GrpForLeafList"),
+ Types.setTypeFor(BaseYangTypes.UINT32_TYPE),
+ List.of("getLeafListFromGroup", "requireLeafListFromGroup"));
+ // yang-data > uses > group > anydata
+ assertYangDataGenType(
+ assertGenType(genTypesMap, PACKAGE + "YangDataWithAnydataFromGroup"),
+ assertGenType(genTypesMap, PACKAGE + "GrpForAnydata"),
+ assertGenType(genTypesMap, PACKAGE + "grp._for.anydata.AnydataFromGroup"),
+ List.of("getAnydataFromGroup", "requireAnydataFromGroup"));
+ // yang-data > uses > group > anyxml
+ assertYangDataGenType(
+ assertGenType(genTypesMap, PACKAGE + "YangDataWithAnyxmlFromGroup"),
+ assertGenType(genTypesMap, PACKAGE + "GrpForAnyxml"),
+ assertGenType(genTypesMap, PACKAGE + "grp._for.anyxml.AnyxmlFromGroup"),
+ List.of("getAnyxmlFromGroup", "requireAnyxmlFromGroup"));
+
+ // ensure module class has only getter for root container
+ final GeneratedType moduleType = assertGenType(genTypesMap, MODULE_CLASS_NAME);
+ assertNotNull(moduleType.getMethodDefinitions());
+ assertEquals(List.of("getRootContainer"),
+ moduleType.getMethodDefinitions().stream().map(MethodSignature::getName)
+ .filter(methodName -> methodName.startsWith("get")).toList());
+
+ // ensure yang-data at non-top level is ignored (no getters in parent container)
+ GeneratedType rootContainerType = assertGenType(genTypesMap, ROOT_CONTAINER_CLASS_NAME);
+ assertNotNull(rootContainerType.getMethodDefinitions());
+ assertTrue(rootContainerType.getMethodDefinitions().stream()
+ .filter(method -> method.getName().startsWith("get"))
+ .findFirst().isEmpty());
+ }
+
+ @Test
+ public void yangDataNamingStrategy() {
+ final List<GeneratedType> allGenTypes = DefaultBindingGenerator.generateFor(
+ YangParserTestUtils.parseYangResources(Mdsal675Test.class,
+ "/yang-data-models/ietf-restconf.yang", "/yang-data-models/yang-data-naming.yang"));
+ assertNotNull(allGenTypes);
+ assertEquals(22, allGenTypes.size());
+ final Set<String> genTypeNames =
+ allGenTypes.stream().map(type -> type.getIdentifier().toString()).collect(Collectors.toSet());
+
+ // template name is not compliant to YANG identifier -> char encoding used, name starts with $ char
+ // a) latin1, but not a valid Java identifier
+ assertTrue(genTypeNames.contains(PACKAGE2 + "$ľaľa$20$ho$2C$$20$papľuha$2C$$20$ogrcal$20$mi$20$krpce$21$"));
+ // b) cyrillic, but a valid Java identifier
+ assertTrue(genTypeNames.contains(PACKAGE2 + "привет"));
+
+ // template name is compliant to yang identifier -> camel-case used
+ assertTrue(genTypeNames.contains(PACKAGE2 + "IdentifierCompliantName"));
+
+ // name collision with a typedef
+ assertTrue(genTypeNames.contains(PACKAGE2 + "Collision1$T"));
+ assertTrue(genTypeNames.contains(PACKAGE2 + "collision1.Collision1"));
+ assertTrue(genTypeNames.contains(PACKAGE2 + "Collision1$YD"));
+
+ // name collision with top level container
+ assertTrue(genTypeNames.contains(PACKAGE2 + "Collision2"));
+ assertTrue(genTypeNames.contains(PACKAGE2 + "Collision2$YD"));
+
+ // name collision with group used
+ assertTrue(genTypeNames.contains(PACKAGE2 + "Collision3$G"));
+ assertTrue(genTypeNames.contains(PACKAGE2 + "Collision3$YD"));
+
+ // rc:yang-data .-/#
+ assertTrue(genTypeNames.contains(PACKAGE2 + "$$2E$$2D$$2F$$23$"));
+ assertTrue(genTypeNames.contains(PACKAGE2 + "$$2e$$2d$$2f$$23$$.Foo"));
+
+ // rc:yang-data -./#
+ assertTrue(genTypeNames.contains(PACKAGE2 + "$$2D$$2E$$2F$$23$"));
+ assertTrue(genTypeNames.contains(PACKAGE2 + "$$2d$$2e$$2f$$23$$.Foo"));
+ }
+
+ private static GeneratedType assertGenType(final Map<String, GeneratedType> genTypesMap, final String className) {
+ final var ret = genTypesMap.get(className);
+ assertNotNull("no type generated: " + className, ret);
+ return ret;
+ }
+
+ private static void assertYangDataGenType(final GeneratedType yangDataType, final Type contentType,
+ final List<String> getterMethods) {
+ assertImplements(yangDataType, BindingTypes.yangData(yangDataType));
+ INTERFACE_METHODS.forEach((name, type) -> assertHasMethod(yangDataType, name, type));
+ for (final String methodName : getterMethods) {
+ assertHasMethod(yangDataType, methodName, contentType);
+ }
+ }
+
+ private static void assertYangDataGenType(final GeneratedType yangDataType, final GeneratedType groupType,
+ final Type contentType, final List<String> getterMethods) {
+ assertImplements(yangDataType, BindingTypes.yangData(yangDataType));
+ assertImplements(yangDataType, groupType);
+ INTERFACE_METHODS.forEach((name, type) -> assertHasMethod(yangDataType, name, type));
+ for (final String methodName : getterMethods) {
+ assertHasMethod(groupType, methodName, contentType);
+ }
+ }
+
+ private static void assertHasMethod(final GeneratedType genType, final String methodName,
+ final Type returnType) {
+ assertTrue("no expected method " + methodName + " returning " + returnType,
+ genType.getMethodDefinitions().stream().anyMatch(
+ method -> methodName.equals(method.getName()) && returnType.equals(method.getReturnType())));
+ }
+
+ private static void assertImplements(final GeneratedType genType, final Type implementedType) {
+ assertThat(implementedType, in(genType.getImplements()));
+ }
+
+}
--- /dev/null
+module ietf-restconf {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-restconf";
+ prefix "rc";
+
+ description
+ "NB: Original file minified for testing purposes";
+
+ revision 2017-01-26;
+
+ extension yang-data {
+ argument name {
+ yin-element true;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module yang-data-demo {
+ yang-version 1.1;
+ namespace "urn:test:yang:data:demo";
+ prefix ydd;
+
+ import ietf-restconf { prefix rc; }
+
+ rc:yang-data yang-data-with-container {
+ container container-from-yang-data {
+ leaf str {
+ type string;
+ }
+ }
+ }
+
+ rc:yang-data yang-data-with-list {
+ list list-from-yang-data {
+ leaf str {
+ type string;
+ }
+ }
+ }
+
+ rc:yang-data yang-data-with-leaf {
+ leaf leaf-from-yang-data {
+ type string;
+ }
+ }
+
+ rc:yang-data yang-data-with-leaf-list {
+ leaf-list leaf-list-from-yang-data {
+ type string;
+ }
+ }
+
+ rc:yang-data yang-data-with-anydata {
+ anydata anydata-from-yang-data;
+ }
+
+ rc:yang-data yang-data-with-anyxml {
+ anyxml anyxml-from-yang-data;
+ }
+
+ rc:yang-data yang-data-with-container-from-group {
+ uses grp-for-container;
+ }
+
+ rc:yang-data yang-data-with-list-from-group {
+ uses grp-for-list;
+ }
+
+ rc:yang-data yang-data-with-leaf-from-group {
+ uses grp-for-leaf;
+ }
+
+ rc:yang-data yang-data-with-leaf-list-from-group {
+ uses grp-for-leaf-list;
+ }
+
+ rc:yang-data yang-data-with-anydata-from-group {
+ uses grp-for-anydata;
+ }
+
+ rc:yang-data yang-data-with-anyxml-from-group {
+ uses grp-for-anyxml;
+ }
+
+ grouping grp-for-container {
+ container container-from-group {
+ leaf str {
+ type string;
+ }
+ }
+ }
+
+ grouping grp-for-list {
+ list list-from-group {
+ leaf num {
+ type uint32;
+ }
+ }
+ }
+
+ grouping grp-for-leaf {
+ leaf leaf-from-group {
+ type uint32;
+ }
+ }
+
+ grouping grp-for-leaf-list {
+ leaf-list leaf-list-from-group {
+ type uint32;
+ }
+ }
+
+ grouping grp-for-anydata {
+ anydata anydata-from-group;
+ }
+
+ grouping grp-for-anyxml{
+ anyxml anyxml-from-group;
+ }
+
+ container root-container {
+ rc:yang-data "yang-data-ignored" {
+ container ignored;
+ }
+ }
+}
--- /dev/null
+module yang-data-naming {
+ yang-version 1.1;
+ namespace "urn:test:yang:data:naming";
+ prefix ydn;
+
+ import ietf-restconf { prefix rc; }
+
+ rc:yang-data "ľaľa ho, papľuha, ogrcal mi krpce!" {
+ container krpce {
+ leaf moje {
+ type boolean;
+ }
+ leaf ogrcanie {
+ type boolean;
+ }
+ }
+ }
+
+ rc:yang-data привет {
+ container cyrillic {
+ leaf ja {
+ type boolean;
+ }
+ }
+ }
+
+ rc:yang-data "identifier-compliant-name" {
+ container cont {
+ leaf lf {
+ type boolean;
+ }
+ }
+ }
+
+ rc:yang-data collision1 {
+ container collision1;
+ }
+
+ typedef collision1 {
+ type string;
+ }
+
+ rc:yang-data collision2 {
+ container some;
+ }
+
+ container collision2;
+
+ rc:yang-data collision3 {
+ uses collision3;
+ }
+
+ grouping collision3 {
+ container some;
+ }
+
+ rc:yang-data .-/# {
+ container foo {
+ leaf bar {
+ type string;
+ }
+ }
+ }
+
+ rc:yang-data -./# {
+ list foo {
+ key baz;
+ leaf baz {
+ type uint32;
+ }
+ }
+ }
+}
* YANG identifier of the statement represented by this class.
*/
public static final «c.type.importedNonNull» «c.name» = «entry.key.importedName».«BindingMapping.MODULE_INFO_QNAMEOF_METHOD_NAME»("«entry.value»");
+ «ELSEIF BindingMapping.NAME_STATIC_FIELD_NAME.equals(c.name)»
+ «val entry = c.value as Entry<JavaTypeName, String>»
+ /**
+ * Yang Data template name of the statement represented by this class.
+ */
+ public static final «c.type.importedNonNull» «c.name» = «entry.key.importedName».«BindingMapping.MODULE_INFO_YANGDATANAMEOF_METHOD_NAME»("«entry.value»");
«ELSEIF BindingMapping.VALUE_STATIC_FIELD_NAME.equals(c.name) && BaseIdentity.equals(c.value)»
«val typeName = c.type.importedName»
«val override = OVERRIDE.importedName»
import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
import org.opendaylight.yangtools.yang.binding.Augmentable;
import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.YangData;
/**
* Transformator of the data from the virtual form to JAVA programming language. The result source code represent java
public final class BuilderGenerator implements CodeGenerator {
private static final JavaTypeName AUGMENTABLE = JavaTypeName.create(Augmentable.class);
private static final JavaTypeName AUGMENTATION = JavaTypeName.create(Augmentation.class);
+ private static final JavaTypeName YANG_DATA = JavaTypeName.create(YangData.class);
/**
* Passes via list of implemented types in <code>type</code>.
for (Type t : generated.getImplements()) {
// "rpc" and "grouping" elements do not implement Augmentable
final JavaTypeName name = t.getIdentifier();
- if (name.equals(AUGMENTABLE) || name.equals(AUGMENTATION)) {
+ if (name.equals(AUGMENTABLE) || name.equals(AUGMENTATION) || name.equals(YANG_DATA)) {
return true;
}
}
import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.MODEL_BINDING_PROVIDER_CLASS_NAME
import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.MODULE_INFO_CLASS_NAME
import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.MODULE_INFO_QNAMEOF_METHOD_NAME
+import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.MODULE_INFO_YANGDATANAMEOF_METHOD_NAME
import com.google.common.base.Preconditions
import com.google.common.collect.ImmutableSet
import java.util.TreeMap
import java.util.function.Function
import org.eclipse.xtend.lib.annotations.Accessors
+import org.opendaylight.yangtools.rfc8040.model.api.YangDataSchemaNode
import org.opendaylight.yangtools.yang.binding.YangModuleInfo
import org.opendaylight.yangtools.yang.common.Revision
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext
val Module module
val EffectiveModelContext ctx
val Function<ModuleLike, Optional<String>> moduleFilePathResolver
+ val boolean hasYangData
var importedTypes = CORE_IMPORT_STR
this.moduleFilePathResolver = moduleFilePathResolver
packageName = module.QNameModule.rootPackageName;
modelBindingProviderName = '''«packageName».«MODEL_BINDING_PROVIDER_CLASS_NAME»'''
+ hasYangData = module.unknownSchemaNodes.stream.anyMatch([s | s instanceof YangDataSchemaNode])
}
def String generate() {
*
* @param localName local name
* @return A QName
- * @throws NullPointerException if {@code localName} is null
- * @throws IllegalArgumentException if localName is not a valid YANG identifier
+ * @throws NullPointerException if {@code localName} is {@code null}
+ * @throws IllegalArgumentException if {@code localName} is not a valid YANG identifier
*/
public static @NonNull QName «MODULE_INFO_QNAMEOF_METHOD_NAME»(final String localName) {
return QName.create(NAME, localName).intern();
}
+ «IF hasYangData»
+
+ /**
+ * Create an interned {@link YangDataName} with specified {@code templateName} and namespace/revision of
+ * this module.
+ *
+ * @param templateName template name
+ * @return A YangDataName
+ * @throws NullPointerException if {@code templateName} is {@code null}
+ * @throws IllegalArgumentException if {@code templateName} is empty
+ */
+ public static @NonNull YangDataName «MODULE_INFO_YANGDATANAMEOF_METHOD_NAME»(final String templateName) {
+ return new YangDataName(NAME.getModule(), templateName).intern();
+ }
+ «ENDIF»
«classBody(module, MODULE_INFO_CLASS_NAME, submodules)»
}
package «packageName»;
«importedTypes»
+ «IF hasYangData»
+ import org.opendaylight.yangtools.yang.common.YangDataName;
+ «ENDIF»
«body»
'''.toString
generateTestSources("/compilation/union-string-pattern", sourcesOutputDir);
CompilationTestUtils.testCompilation(sourcesOutputDir, compiledOutputDir);
- final ClassLoader loader = new URLClassLoader(new URL[] { compiledOutputDir.toURI().toURL() });
+ final ClassLoader loader = new URLClassLoader(new URL[]{compiledOutputDir.toURI().toURL()});
final Class<?> fooClass = Class.forName(CompilationTestUtils.BASE_PKG + ".foo.norev.Foo", true, loader);
final Field patterns = fooClass.getDeclaredField(TypeConstants.PATTERN_CONSTANT_NAME);
CompilationTestUtils.cleanUp(sourcesOutputDir, compiledOutputDir);
}
+ @Test
+ public void yangDataCompilation() throws Exception {
+ final File sourcesOutputDir = CompilationTestUtils.generatorOutput("yang-data-gen");
+ final File compiledOutputDir = CompilationTestUtils.compilerOutput("yang-data-gen");
+
+ generateTestSources("/compilation/yang-data-gen", sourcesOutputDir);
+ CompilationTestUtils.testCompilation(sourcesOutputDir, compiledOutputDir);
+
+ final ClassLoader loader = new URLClassLoader(new URL[]{compiledOutputDir.toURI().toURL()});
+ final List<String> artifactNames = List.of(
+ // module with top level container
+ "$YangModuleInfoImpl", "YangDataDemoData", "RootContainer", "RootContainerBuilder",
+
+ // yang-data artifacts
+ "YangDataWithContainer", "YangDataWithContainerBuilder",
+ "YangDataWithList", "YangDataWithListBuilder",
+ "YangDataWithLeaf", "YangDataWithLeafBuilder",
+ "YangDataWithLeafList", "YangDataWithLeafListBuilder",
+ "YangDataWithAnydata", "YangDataWithAnydataBuilder",
+ "YangDataWithAnyxml", "YangDataWithAnyxmlBuilder",
+
+ // yang-data content artifacts
+ "yang.data.with.container.ContainerFromYangData",
+ "yang.data.with.container.ContainerFromYangDataBuilder",
+ "yang.data.with.list.ListFromYangData", "yang.data.with.list.ListFromYangDataBuilder",
+ "yang.data.with.anydata.AnydataFromYangData", "yang.data.with.anyxml.AnyxmlFromYangData",
+
+ // yang-data artifacts using groups
+ "YangDataWithContainerFromGroup", "YangDataWithContainerFromGroupBuilder",
+ "YangDataWithListFromGroup", "YangDataWithListFromGroupBuilder",
+ "YangDataWithLeafFromGroup", "YangDataWithLeafFromGroupBuilder",
+ "YangDataWithLeafListFromGroup", "YangDataWithLeafListFromGroupBuilder",
+ "YangDataWithAnydataFromGroup", "YangDataWithAnydataFromGroupBuilder",
+ "YangDataWithAnyxmlFromGroup", "YangDataWithAnyxmlFromGroupBuilder",
+
+ // group artifacts
+ "GrpForContainer", "GrpForList", "GrpForLeaf", "GrpForLeafList", "GrpForAnydata", "GrpForAnyxml",
+
+ // group content artifacts
+ "grp._for.container.ContainerFromGroup", "grp._for.container.ContainerFromGroupBuilder",
+ "grp._for.list.ListFromGroup", "grp._for.list.ListFromGroupBuilder",
+ "grp._for.anydata.AnydataFromGroup", "grp._for.anyxml.AnyxmlFromGroup",
+
+ // artifacts for non-ascii template naming: yang data artifact, inner container + builder
+ "$ľaľaho$20$papľuhu", "$ľaľaho$20$papľuhuBuilder",
+ "$ľaľaho$20$papľuhu$.LatinNaming", "$ľaľaho$20$papľuhu$.LatinNamingBuilder",
+ "привет", "приветBuilder", "привет$.CyrillicNaming", "привет$.CyrillicNamingBuilder"
+ );
+
+ for (String name : artifactNames) {
+ final String className = CompilationTestUtils.BASE_PKG + ".urn.test.yang.data.demo.rev220222." + name;
+ // ensure class source is generated
+ final String srcPath = className.replace('.', File.separatorChar) + ".java";
+ assertTrue(srcPath + " exists", new File(sourcesOutputDir, srcPath).exists());
+ // ensure class is loadable
+ Class.forName(className, true, loader);
+ }
+
+ CompilationTestUtils.cleanUp(sourcesOutputDir, compiledOutputDir);
+ }
+
private static void testReturnTypeIdentityref(final Class<?> clazz, final String methodName,
final String returnTypeStr) throws NoSuchMethodException {
Class<?> returnType = clazz.getMethod(methodName).getReturnType();
--- /dev/null
+module ietf-restconf {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-restconf";
+ prefix "rc";
+
+ description
+ "NB: Original file minified for testing purposes";
+
+ revision 2017-01-26;
+
+ extension yang-data {
+ argument name {
+ yin-element true;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module yang-data-demo {
+ yang-version 1.1;
+ namespace "urn:test:yang:data:demo";
+ prefix ydd;
+
+ import ietf-restconf {
+ prefix rc;
+ }
+
+ revision 2022-02-22;
+
+ rc:yang-data yang-data-with-container {
+ container container-from-yang-data {
+ leaf str {
+ type string;
+ }
+ }
+ }
+
+ rc:yang-data yang-data-with-list {
+ list list-from-yang-data {
+ leaf str {
+ type string;
+ }
+ }
+ }
+
+ rc:yang-data yang-data-with-leaf {
+ leaf leaf-from-yang-data {
+ type string;
+ }
+ }
+
+ rc:yang-data yang-data-with-leaf-list {
+ leaf-list leaf-list-from-yang-data {
+ type string;
+ }
+ }
+
+ rc:yang-data yang-data-with-anydata {
+ anydata anydata-from-yang-data;
+ }
+
+ rc:yang-data yang-data-with-anyxml {
+ anyxml anyxml-from-yang-data;
+ }
+
+ rc:yang-data yang-data-with-container-from-group {
+ uses grp-for-container;
+ }
+
+ rc:yang-data yang-data-with-list-from-group {
+ uses grp-for-list;
+ }
+
+ rc:yang-data yang-data-with-leaf-from-group {
+ uses grp-for-leaf;
+ }
+
+ rc:yang-data yang-data-with-leaf-list-from-group {
+ uses grp-for-leaf-list;
+ }
+
+ rc:yang-data yang-data-with-anydata-from-group {
+ uses grp-for-anydata;
+ }
+
+ rc:yang-data yang-data-with-anyxml-from-group {
+ uses grp-for-anyxml;
+ }
+
+ grouping grp-for-container {
+ container container-from-group {
+ leaf str {
+ type string;
+ }
+ }
+ }
+
+ grouping grp-for-list {
+ list list-from-group {
+ leaf num {
+ type uint32;
+ }
+ }
+ }
+
+ grouping grp-for-leaf {
+ leaf leaf-from-group {
+ type uint32;
+ }
+ }
+
+ grouping grp-for-leaf-list {
+ leaf-list leaf-list-from-group {
+ type uint32;
+ }
+ }
+
+ grouping grp-for-anydata {
+ anydata anydata-from-group;
+ }
+
+ grouping grp-for-anyxml {
+ anyxml anyxml-from-group;
+ }
+
+ container root-container {
+ rc:yang-data "yang-data-ignored";
+ }
+
+ rc:yang-data "ľaľaho papľuhu" {
+ container latin-naming;
+ }
+
+ rc:yang-data привет {
+ container cyrillic-naming;
+ }
+}
import org.opendaylight.yangtools.yang.binding.RpcService;
import org.opendaylight.yangtools.yang.binding.ScalarTypeObject;
import org.opendaylight.yangtools.yang.binding.UnionTypeObject;
+import org.opendaylight.yangtools.yang.binding.YangData;
import org.opendaylight.yangtools.yang.binding.YangFeature;
import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.YangDataName;
import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
public final class BindingTypes {
public static final ConcreteType UNION_TYPE_OBJECT = typeForClass(UnionTypeObject.class);
public static final ConcreteType INSTANCE_IDENTIFIER = typeForClass(InstanceIdentifier.class);
public static final ConcreteType KEYED_INSTANCE_IDENTIFIER = typeForClass(KeyedInstanceIdentifier.class);
+ public static final ConcreteType YANG_DATA_NAME = typeForClass(YangDataName.class);
// This is an annotation, we are current just referencing the type
public static final JavaTypeName ROUTING_CONTEXT = JavaTypeName.create(RoutingContext.class);
private static final ConcreteType RPC = typeForClass(Rpc.class);
private static final ConcreteType RPC_RESULT = typeForClass(RpcResult.class);
private static final ConcreteType YANG_FEATURE = typeForClass(YangFeature.class);
+ private static final ConcreteType YANG_DATA = typeForClass(YangData.class);
private BindingTypes() {
* @param input Type input type
* @param output Type output type
* @return A parameterized type corresponding to {@code Action<Parent, Input, Output>}
- * @throws NullPointerException if any argument is is null
+ * @throws NullPointerException if any argument is {@code null}
*/
public static ParameterizedType action(final Type parent, final Type input, final Type output) {
return parameterizedTypeFor(ACTION, instanceIdentifier(parent), input, output);
* @param input Type input type
* @param output Type output type
* @return A parameterized type corresponding to {@code KeyedListAction<ParentKey, Parent, Input, Output>}
- * @throws NullPointerException if any argument is is null
+ * @throws NullPointerException if any argument is {@code null}
*/
public static ParameterizedType keyedListAction(final Type parent, final Type keyType, final Type input,
final Type output) {
*
* @param concreteType The concrete type of this notification
* @return A parameterized type corresponding to {@code Notification<ConcreteType>}
- * @throws NullPointerException if any argument is is null
+ * @throws NullPointerException if any argument is {@code null}
*/
public static ParameterizedType notification(final Type concreteType) {
return parameterizedTypeFor(NOTIFICATION, concreteType);
* @param concreteType The concrete type of this notification
* @param parent Type of parent defining the notification
* @return A parameterized type corresponding to {@code InstanceNotification<ConcreteType, Parent>}
- * @throws NullPointerException if {@code parent} is is null
+ * @throws NullPointerException if {@code parent} is {@code null}
*/
public static ParameterizedType instanceNotification(final Type concreteType, final Type parent) {
return parameterizedTypeFor(INSTANCE_NOTIFICATION, concreteType, parent);
* @param parent Type of parent defining the notification
* @param keyType Type of parent's key
* @return A parameterized type corresponding to {@code KeyedInstanceNotification<ConcreteType, ParentKey, Parent>}
- * @throws NullPointerException if any argument is is null
+ * @throws NullPointerException if any argument is {@code null}
*/
public static ParameterizedType keyedListNotification(final Type concreteType, final Type parent,
final Type keyType) {
*
* @param type Type for which to specialize
* @return A parameterized type corresponding to {@code Augmentable<Type>}
- * @throws NullPointerException if {@code type} is null
+ * @throws NullPointerException if {@code type} is {@code null}
*/
public static @NonNull ParameterizedType augmentable(final Type type) {
return parameterizedTypeFor(AUGMENTABLE, type);
*
* @param type Type for which to specialize
* @return A parameterized type corresponding to {@code Augmentation<Type>}
- * @throws NullPointerException if {@code type} is null
+ * @throws NullPointerException if {@code type} is {@code null}
*/
public static @NonNull ParameterizedType augmentation(final Type type) {
return parameterizedTypeFor(AUGMENTATION, type);
*
* @param type Type for which to specialize
* @return A parameterized type corresponding to {@code ChildOf<Type>}
- * @throws NullPointerException if {@code type} is null
+ * @throws NullPointerException if {@code type} is {@code null}
*/
public static ParameterizedType childOf(final Type type) {
return parameterizedTypeFor(CHILD_OF, type);
*
* @param type Type for which to specialize
* @return A parameterized type corresponding to {@code ChoiceIn<Type>}
- * @throws NullPointerException if {@code type} is null
+ * @throws NullPointerException if {@code type} is {@code null}
*/
public static ParameterizedType choiceIn(final Type type) {
return parameterizedTypeFor(CHOICE_IN, type);
*
* @param type Type for which to specialize
* @return A parameterized type corresponding to {@code Identifier<Type>}
- * @throws NullPointerException if {@code type} is null
+ * @throws NullPointerException if {@code type} is {@code null}
*/
public static ParameterizedType identifier(final Type type) {
return parameterizedTypeFor(IDENTIFIER, type);
*
* @param type Type for which to specialize
* @return A parameterized type corresponding to {@code Identifiable<Type>}
- * @throws NullPointerException if {@code type} is null
+ * @throws NullPointerException if {@code type} is {@code null}
*/
public static ParameterizedType identifiable(final Type type) {
return parameterizedTypeFor(IDENTIFIABLE, type);
*
* @param type Type for which to specialize
* @return A parameterized type corresponding to {@code InstanceIdentifier<Type>}
- * @throws NullPointerException if {@code type} is null
+ * @throws NullPointerException if {@code type} is {@code null}
*/
public static ParameterizedType instanceIdentifier(final Type type) {
return parameterizedTypeFor(INSTANCE_IDENTIFIER, type);
* @param type Type for which to specialize
* @param keyType Type of key
* @return A parameterized type corresponding to {@code KeyedInstanceIdentifier<Type, KeyType>}
- * @throws NullPointerException if any argument is is null
+ * @throws NullPointerException if any argument is is {@code null}
*/
public static ParameterizedType keyedInstanceIdentifier(final Type type, final Type keyType) {
return parameterizedTypeFor(KEYED_INSTANCE_IDENTIFIER, type, keyType);
*
* @param type Type for which to specialize
* @return A parameterized type corresponding to {@code OpaqueObject<Type>}
- * @throws NullPointerException if {@code type} is null
+ * @throws NullPointerException if {@code type} is {@code null}
*/
public static ParameterizedType opaqueObject(final Type type) {
return parameterizedTypeFor(OPAQUE_OBJECT, type);
*
* @param type Type for which to specialize
* @return A parameterized type corresponding to {@code RpcResult<Type>}
- * @throws NullPointerException if {@code type} is null
+ * @throws NullPointerException if {@code type} is {@code null}
*/
public static ParameterizedType rpcResult(final Type type) {
return parameterizedTypeFor(RPC_RESULT, type);
*
* @param type Type for which to specialize
* @return A parameterized type corresponding to {@code ScalarTypeObject<Type>}
- * @throws NullPointerException if {@code type} is null
+ * @throws NullPointerException if {@code type} is {@code null}
*/
public static ParameterizedType scalarTypeObject(final Type type) {
return parameterizedTypeFor(SCALAR_TYPE_OBJECT, type);
}
+ /**
+ * Type specializing {@link YangData} for a particular type.
+ *
+ * @param concreteType The concrete type of this notification
+ * @return A parameterized type corresponding to {@code YangData<Type>}
+ * @throws NullPointerException if any argument is is {@code null}
+ */
+ public static ParameterizedType yangData(final Type concreteType) {
+ return parameterizedTypeFor(YANG_DATA, concreteType);
+ }
+
/**
* Type specializing {@link YangFeature} for a particular type.
*
* @param concreteType The concrete type of this notification
* @param parent Type of parent defining the notification
* @return A parameterized type corresponding to {@code YangFeature<Type, DataRootType>}
- * @throws NullPointerException if any argument is is null
+ * @throws NullPointerException if any argument is is {@code null}
*/
public static ParameterizedType yangFeature(final Type concreteType, final Type parent) {
return parameterizedTypeFor(YANG_FEATURE, concreteType, parent);
*
* @param type Parameterized type
* @return Augmentable target, or null if {@code type} does not match the result of {@link #augmentation(Type)}
- * @throws NullPointerException if {@code type} is null
+ * @throws NullPointerException if {@code type} is {@code null}
*/
@Beta
public static @Nullable Type extractAugmentable(final ParameterizedType type) {
*
* @param type Parameterized type
* @return Identifiable target, or null if {@code type} does not match the result of {@link #identifier(Type)}
- * @throws NullPointerException if {@code type} is null
+ * @throws NullPointerException if {@code type} is {@code null}
*/
@Beta
public static @Nullable Type extractIdentifiable(final ParameterizedType type) {
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-model-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>rfc8040-model-api</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-repo-api</artifactId>
requires transitive org.opendaylight.yangtools.yang.binding;
requires transitive org.opendaylight.yangtools.yang.repo.api;
requires transitive org.opendaylight.yangtools.yang.repo.spi;
+ requires transitive org.opendaylight.yangtools.rfc8040.model.api;
requires transitive org.opendaylight.mdsal.binding.model.api;
requires org.slf4j;
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech s.r.o. 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.mdsal.binding.runtime.api;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.rfc8040.model.api.YangDataEffectiveStatement;
+
+@NonNullByDefault
+public interface YangDataRuntimeType extends CompositeRuntimeType {
+ @Override
+ YangDataEffectiveStatement statement();
+}
public static final @NonNull String NOTIFICATION_LISTENER_SUFFIX = "Listener";
public static final @NonNull String BUILDER_SUFFIX = "Builder";
public static final @NonNull String KEY_SUFFIX = "Key";
+ // ietf-restconf:yang-data, i.e. YangDataName
+ public static final @NonNull String NAME_STATIC_FIELD_NAME = "NAME";
+ // everything that can have a QName (e.g. identifier bound to a namespace)
public static final @NonNull String QNAME_STATIC_FIELD_NAME = "QNAME";
+ // concrete extensible contracts, for example 'feature', 'identity' and similar
public static final @NonNull String VALUE_STATIC_FIELD_NAME = "VALUE";
public static final @NonNull String PACKAGE_PREFIX = "org.opendaylight.yang.gen.v1";
public static final @NonNull String AUGMENTATION_FIELD = "augmentation";
public static final @NonNull String MODULE_INFO_CLASS_NAME = "$YangModuleInfoImpl";
public static final @NonNull String MODULE_INFO_QNAMEOF_METHOD_NAME = "qnameOf";
+ public static final @NonNull String MODULE_INFO_YANGDATANAMEOF_METHOD_NAME = "yangDataNameOf";
public static final @NonNull String MODEL_BINDING_PROVIDER_CLASS_NAME = "$YangModelBindingProvider";
/**
return javaToYang.inverse();
}
+ /**
+ * Builds class name representing yang-data template name which is not yang identifier compliant.
+ *
+ * @param templateName template name
+ * @return Java class name
+ * @throws NullPointerException if {@code templateName} is {@code null}
+ * @throws IllegalArgumentException if (@code templateName} is empty
+ */
+ // TODO: take YangDataName once we have it readily available
+ public static String mapYangDataName(final String templateName) {
+ return mapEnumAssignedName(templateName);
+ }
+
// See https://docs.oracle.com/javase/specs/jls/se16/html/jls-3.html#jls-3.8
// TODO: we are being conservative here, but should differentiate TypeIdentifier and UnqualifiedMethodIdentifier,
// which have different exclusions
assertEquals(expected, BindingMapping.mapEnumAssignedNames(yang));
}
+
+ @Test
+ public void yangDataMapping() {
+ // single ascii compliant non-conflicting word - remain as is
+ assertEquals("single", BindingMapping.mapYangDataName("single"));
+ // ascii compliant - non-compliany chars only encoded
+ assertEquals("$abc$20$cde", BindingMapping.mapYangDataName("abc cde"));
+ // latin1 compliant -> latin chars normalized, non-compliant chars are encoded
+ assertEquals("$ľaľaho$20$papľuhu", BindingMapping.mapYangDataName("ľaľaho papľuhu"));
+ // latin1 non-compliant - all non-compliant characters encoded
+ assertEquals("$привет$20$papľuhu", BindingMapping.mapYangDataName("привет papľuhu"));
+ }
}
<groupId>org.opendaylight.mdsal.model</groupId>
<artifactId>yang-ext</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal.binding.model.ietf</groupId>
+ <artifactId>rfc8040</artifactId>
+ </dependency>
<dependency>
<groupId>org.kohsuke.metainf-services</groupId>
<artifactId>metainf-services</artifactId>
--- /dev/null
+module yang-data-demo {
+ yang-version 1.1;
+ namespace "urn:test:yang:data:demo";
+ prefix ydd;
+
+ import ietf-restconf {
+ prefix rc;
+ }
+
+ revision 2022-02-22;
+
+ rc:yang-data yang-data-with-container {
+ container container-from-yang-data {
+ leaf str {
+ type string;
+ }
+ }
+ }
+
+ rc:yang-data yang-data-with-list {
+ list list-from-yang-data {
+ leaf str {
+ type string;
+ }
+ }
+ }
+
+ rc:yang-data yang-data-with-leaf {
+ leaf leaf-from-yang-data {
+ type string;
+ }
+ }
+
+ rc:yang-data yang-data-with-leaf-list {
+ leaf-list leaf-list-from-yang-data {
+ type string;
+ }
+ }
+
+ rc:yang-data yang-data-with-anydata {
+ anydata anydata-from-yang-data;
+ }
+
+ rc:yang-data yang-data-with-anyxml {
+ anyxml anyxml-from-yang-data;
+ }
+
+ rc:yang-data yang-data-with-container-from-group {
+ uses grp-for-container;
+ }
+
+ rc:yang-data yang-data-with-list-from-group {
+ uses grp-for-list;
+ }
+
+ rc:yang-data yang-data-with-leaf-from-group {
+ uses grp-for-leaf;
+ }
+
+ rc:yang-data yang-data-with-leaf-list-from-group {
+ uses grp-for-leaf-list;
+ }
+
+ rc:yang-data yang-data-with-anydata-from-group {
+ uses grp-for-anydata;
+ }
+
+ rc:yang-data yang-data-with-anyxml-from-group {
+ uses grp-for-anyxml;
+ }
+
+ grouping grp-for-container {
+ container container-from-group {
+ leaf str {
+ type string;
+ }
+ }
+ }
+
+ grouping grp-for-list {
+ list list-from-group {
+ leaf num {
+ type uint32;
+ }
+ }
+ }
+
+ grouping grp-for-leaf {
+ leaf leaf-from-group {
+ type uint32;
+ }
+ }
+
+ grouping grp-for-leaf-list {
+ leaf-list leaf-list-from-group {
+ type uint32;
+ }
+ }
+
+ grouping grp-for-anydata {
+ anydata anydata-from-group;
+ }
+
+ grouping grp-for-anyxml{
+ anyxml anyxml-from-group;
+ }
+
+ container root-container {
+ rc:yang-data "yang-data-ignored";
+ }
+
+ rc:yang-data ./# {
+ container foo;
+ }
+
+ rc:yang-data /.# {
+ list foo;
+ }
+}
}
/**
- * Utility method for checking whether a target object is a compatible DataObject.
+ * Utility method for checking whether a target object is a compatible {@link BindingContract}.
*
- * @param requiredClass Required DataObject class
+ * @param requiredClass Required BindingContract class
* @param obj Object to check, may be null
* @return Object cast to required class, if its implemented class matches requirement, null otherwise
* @throws NullPointerException if {@code requiredClass} is null
*/
- public static <T extends DataObject> @Nullable T checkCast(final @NonNull Class<T> requiredClass,
+ public static <T extends BindingContract<?>> @Nullable T checkCast(final @NonNull Class<T> requiredClass,
final @Nullable Object obj) {
- return obj instanceof DataObject && requiredClass.equals(((DataObject) obj).implementedInterface())
+ return obj instanceof BindingContract<?> contract && requiredClass.equals(contract.implementedInterface())
? requiredClass.cast(obj) : null;
}