From a2c40d7d57a7a0e32db3e98d6fc224d26d8f2000 Mon Sep 17 00:00:00 2001 From: "Konstantin.Nosach" Date: Wed, 25 Nov 2020 14:07:32 +0200 Subject: [PATCH] Add non-null getters for leaf objects DTO getters inherently return nullable objects. We have a nonnullFoo() concept, which is very powerful with Lists/Maps, where we return an empty collection. This turns out to be very useful for end users, who can worry about other logic problems instead. This patch introduces a similar construct for non-complex getters, so that container foo { leaf bar { type string; } } ends up generating a default method: interface Foo { default @NonNull String requireBar() { // .. } } which nicely throws an exception distinct from NPE and retaining call site. JIRA: MDSAL-602 Change-Id: Ia66ee61f51fb9b6eb11aea3736e5246aadad6a3e Signed-off-by: Kostiantyn Nosach Signed-off-by: Robert Varga --- .../reactor/AbstractExplicitGenerator.java | 24 +++- .../reactor/AbstractTypeAwareGenerator.java | 5 + .../generator/impl/reactor/ListGenerator.java | 5 + .../impl/reactor/OpaqueObjectGenerator.java | 7 + .../impl/AugmentRelativeXPathTest.java | 4 +- .../generator/impl/GenEnumResolvingTest.java | 4 +- .../generator/impl/GeneratedTypesTest.java | 12 +- .../generator/impl/IdentityrefTypeTest.java | 3 +- .../binding/generator/impl/Mdsal320Test.java | 6 +- .../binding/generator/impl/UsesTest.java | 120 ++++++++++-------- .../api/generator/InterfaceTemplate.xtend | 34 ++++- .../java/api/generator/JavaFileTemplate.java | 7 + .../java/api/generator/CompilationTest.java | 4 + .../binding/spec/naming/BindingMapping.java | 18 +++ .../yangtools/yang/binding/CodeHelpers.java | 17 +++ 15 files changed, 197 insertions(+), 73 deletions(-) diff --git a/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractExplicitGenerator.java b/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractExplicitGenerator.java index 0ee1c8b2c9..9a59a88f14 100644 --- a/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractExplicitGenerator.java +++ b/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractExplicitGenerator.java @@ -15,6 +15,7 @@ import com.google.common.base.MoreObjects.ToStringHelper; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.mdsal.binding.generator.impl.reactor.CollisionDomain.Member; +import org.opendaylight.mdsal.binding.model.api.MethodSignature.ValueMechanics; import org.opendaylight.mdsal.binding.model.api.Type; import org.opendaylight.mdsal.binding.model.api.TypeMemberComment; import org.opendaylight.mdsal.binding.model.api.type.builder.AnnotableTypeBuilder; @@ -167,13 +168,18 @@ public abstract class AbstractExplicitGenerator builder, final Type returnType) { - final MethodSignatureBuilder getMethod = builder - .addMethod(BindingMapping.getGetterMethodName(localName().getLocalName())) - .setReturnType(returnType); + return constructGetter(builder, returnType, BindingMapping.getGetterMethodName(localName().getLocalName())); + } + + final MethodSignatureBuilder constructGetter(final GeneratedTypeBuilderBase builder, + final Type returnType, final String methodName) { + final MethodSignatureBuilder getMethod = builder.addMethod(methodName).setReturnType(returnType); annotateDeprecatedIfNecessary(getMethod); @@ -183,6 +189,16 @@ public abstract class AbstractExplicitGenerator builder, final Type returnType) { + // No-op in most cases + } + + final void constructRequireImpl(final GeneratedTypeBuilderBase builder, final Type returnType) { + constructGetter(builder, returnType, BindingMapping.getRequireMethodName(localName().getLocalName())) + .setDefault(true) + .setMechanics(ValueMechanics.NONNULL); + } + void addAsGetterMethodOverride(final @NonNull GeneratedTypeBuilderBase builder, final @NonNull TypeBuilderFactory builderFactory) { // No-op for most cases diff --git a/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractTypeAwareGenerator.java b/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractTypeAwareGenerator.java index 3579197ae3..16a1c1fbc9 100644 --- a/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractTypeAwareGenerator.java +++ b/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractTypeAwareGenerator.java @@ -84,4 +84,9 @@ abstract class AbstractTypeAwareGenerator builder, final Type returnType) { + constructRequireImpl(builder, returnType); + } } diff --git a/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/ListGenerator.java b/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/ListGenerator.java index 890621c216..9138f0bd95 100644 --- a/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/ListGenerator.java +++ b/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/ListGenerator.java @@ -107,4 +107,9 @@ final class ListGenerator extends AbstractCompositeGenerator builder, final Type returnType) { + // No-op + } } diff --git a/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/OpaqueObjectGenerator.java b/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/OpaqueObjectGenerator.java index 8a68246f99..da474e2cc4 100644 --- a/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/OpaqueObjectGenerator.java +++ b/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/OpaqueObjectGenerator.java @@ -8,7 +8,9 @@ package org.opendaylight.mdsal.binding.generator.impl.reactor; import org.opendaylight.mdsal.binding.model.api.GeneratedType; +import org.opendaylight.mdsal.binding.model.api.Type; 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.util.BindingTypes; import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement; import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; @@ -43,4 +45,9 @@ final class OpaqueObjectGenerator> exten return builder.build(); } + + @Override + void constructRequire(final GeneratedTypeBuilderBase builder, final Type returnType) { + constructRequireImpl(builder, returnType); + } } diff --git a/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/AugmentRelativeXPathTest.java b/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/AugmentRelativeXPathTest.java index 7502807e32..9c2c03e7df 100644 --- a/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/AugmentRelativeXPathTest.java +++ b/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/AugmentRelativeXPathTest.java @@ -56,7 +56,7 @@ public class AugmentRelativeXPathTest { final List gtInterfaceMethods = gtInterface.getMethodDefinitions(); assertNotNull("Interface methods are null", gtInterfaceMethods); - assertEquals(7, gtInterfaceMethods.size()); + assertEquals(9, gtInterfaceMethods.size()); MethodSignature getIfcKeyMethod = null; for (final MethodSignature method : gtInterfaceMethods) { @@ -76,7 +76,7 @@ public class AugmentRelativeXPathTest { final List tunnelMethods = gtTunnel.getMethodDefinitions(); assertNotNull("Tunnel methods are null", tunnelMethods); - assertEquals(6, tunnelMethods.size()); + assertEquals(7, tunnelMethods.size()); MethodSignature getTunnelKeyMethod = null; for (MethodSignature method : tunnelMethods) { diff --git a/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/GenEnumResolvingTest.java b/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/GenEnumResolvingTest.java index ec76970eed..397165370b 100644 --- a/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/GenEnumResolvingTest.java +++ b/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/GenEnumResolvingTest.java @@ -65,7 +65,7 @@ public class GenEnumResolvingTest { assertNotNull("Generated Interface cannot contain NULL reference for Method Signature Definitions!", methods); // FIXME: split this into getter/default/static asserts - assertEquals(19, methods.size()); + assertEquals(32, methods.size()); Enumeration ianaIfType = null; for (final MethodSignature method : methods) { if (method.getName().equals("getType")) { @@ -117,7 +117,7 @@ public class GenEnumResolvingTest { assertNotNull("Generated Type Interface cannot contain NULL reference to Enumeration types!", methods); // FIXME: split this into getter/default/static asserts - assertEquals(9, methods.size()); + assertEquals(13, methods.size()); for (final MethodSignature method : methods) { if (method.getName().equals("getLinkUpDownTrapEnable")) { linkUpDownTrapEnable = method.getReturnType(); diff --git a/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/GeneratedTypesTest.java b/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/GeneratedTypesTest.java index caf1d27a5d..9a268e471d 100644 --- a/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/GeneratedTypesTest.java +++ b/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/GeneratedTypesTest.java @@ -51,9 +51,9 @@ public class GeneratedTypesTest { assertNotNull(simpleContainer); assertNotNull(nestedContainer); // FIXME: split this into getter/default/static asserts - assertEquals(7, simpleContainer.getMethodDefinitions().size()); + assertEquals(9, simpleContainer.getMethodDefinitions().size()); // FIXME: split this into getter/default/static asserts - assertEquals(6, nestedContainer.getMethodDefinitions().size()); + assertEquals(8, nestedContainer.getMethodDefinitions().size()); int getFooMethodCounter = 0; int getBarMethodCounter = 0; @@ -134,9 +134,9 @@ public class GeneratedTypesTest { assertNotNull(simpleContainer); assertNotNull(nestedContainer); // FIXME: split this into getter/default/static asserts - assertEquals(7, simpleContainer.getMethodDefinitions().size()); + assertEquals(9, simpleContainer.getMethodDefinitions().size()); // FIXME: split this into getter/default/static asserts - assertEquals(6, nestedContainer.getMethodDefinitions().size()); + assertEquals(8, nestedContainer.getMethodDefinitions().size()); int getFooMethodCounter = 0; int getBarMethodCounter = 0; @@ -286,7 +286,7 @@ public class GeneratedTypesTest { // FIXME: split this into getter/default/static asserts assertEquals(6, listParentContainerMethodsCount); // FIXME: split this into getter/default/static asserts - assertEquals(5, listChildContainerMethodsCount); + assertEquals(6, listChildContainerMethodsCount); assertEquals(1, getSimpleListKeyMethodCount); assertEquals(1, listKeyClassCount); @@ -301,7 +301,7 @@ public class GeneratedTypesTest { assertEquals(1, getBarMethodCount); // FIXME: split this into getter/default/static asserts - assertEquals(10, simpleListMethodsCount); + assertEquals(14, simpleListMethodsCount); } @Test diff --git a/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/IdentityrefTypeTest.java b/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/IdentityrefTypeTest.java index 8f51e8ae4b..3a9e063078 100644 --- a/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/IdentityrefTypeTest.java +++ b/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/IdentityrefTypeTest.java @@ -62,10 +62,11 @@ public class IdentityrefTypeTest { .orElseThrow(); List methodSignatures = moduleGenType.getMethodDefinitions(); - assertEquals(1, methodSignatures.size()); + assertEquals(2, methodSignatures.size()); MethodSignature methodSignature = methodSignatures.get(0); assertEquals("getLf", methodSignature.getName()); + assertEquals("requireLf", methodSignatures.get(1).getName()); Type returnType = methodSignature.getReturnType(); assertThat(returnType, instanceOf(ParameterizedType.class)); diff --git a/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal320Test.java b/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal320Test.java index 10c909f658..d5009b4e42 100644 --- a/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal320Test.java +++ b/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal320Test.java @@ -8,6 +8,7 @@ package org.opendaylight.mdsal.binding.generator.impl; import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -65,14 +66,15 @@ public class Mdsal320Test { assertEquals(BindingMapping.BINDING_EQUALS_NAME, bindingEquals.getName()); final MethodSignature bindingToString = it.next(); assertEquals(BindingMapping.BINDING_TO_STRING_NAME, bindingToString.getName()); - final MethodSignature getBar = it.next(); - assertFalse(it.hasNext()); final Type getBarType = getBar.getReturnType(); assertTrue(getBarType instanceof GeneratedTransferObject); final GeneratedTransferObject getBarTO = (GeneratedTransferObject) getBarType; assertTrue(getBarTO.isUnionType()); assertEquals(bar, getBarTO); + final MethodSignature requireBar = it.next(); + assertThat(requireBar.getName(), startsWith(BindingMapping.REQUIRE_PREFIX)); + assertFalse(it.hasNext()); final GeneratedProperty bar1Prop = bar.getProperties().stream().filter(prop -> "bar$1".equals(prop.getName())) .findFirst().get(); diff --git a/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/UsesTest.java b/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/UsesTest.java index 621a3b3f1c..30596c02ea 100644 --- a/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/UsesTest.java +++ b/binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/UsesTest.java @@ -105,10 +105,11 @@ public class UsesTest { // FIXME: split this into getter/default/static asserts assertEquals(4, caseC.getMethodDefinitions().size()); - assertEquals("Number of method in GroupingCaseTest is incorrect", 2, groupingCaseTest.getMethodDefinitions() + assertEquals("Number of method in GroupingCaseTest is incorrect", 3, groupingCaseTest.getMethodDefinitions() .size()); - containsMethods(groupingCaseTest.getMethodDefinitions(), new NameTypePattern("getLeafGroupingCaseTest1", - "String")); + containsMethods(groupingCaseTest.getMethodDefinitions(), + new NameTypePattern("getLeafGroupingCaseTest1", "String"), + new NameTypePattern("requireLeafGroupingCaseTest1", "String")); } @Test @@ -147,16 +148,20 @@ public class UsesTest { containsInterface("GroupingContainerTest", containerTest); - assertEquals("Number of method in GroupingContainerTestis incorrect", 3, groupingContainerTest + assertEquals("Number of method in GroupingContainerTestis incorrect", 5, groupingContainerTest .getMethodDefinitions().size()); // FIXME: split this into getter/default/static asserts - assertEquals(5, containerTest.getMethodDefinitions().size()); + assertEquals(6, containerTest.getMethodDefinitions().size()); - containsMethods(groupingContainerTest.getMethodDefinitions(), new NameTypePattern( - "getLeafGroupingContainerTest1", "String"), new NameTypePattern("getLeafGroupingContainerTest2", - "Uint8")); + containsMethods(groupingContainerTest.getMethodDefinitions(), + new NameTypePattern("getLeafGroupingContainerTest1", "String"), + new NameTypePattern("requireLeafGroupingContainerTest1", "String"), + new NameTypePattern("getLeafGroupingContainerTest2", "Uint8"), + new NameTypePattern("requireLeafGroupingContainerTest2", "Uint8")); - containsMethods(containerTest.getMethodDefinitions(), new NameTypePattern("getContainerLeafTest", "String")); + containsMethods(containerTest.getMethodDefinitions(), + new NameTypePattern("getContainerLeafTest", "String"), + new NameTypePattern("requireContainerLeafTest", "String")); } @Test @@ -194,14 +199,17 @@ public class UsesTest { containsInterface("GroupingGroupingTest", groupingTest); - assertEquals("Number of method in GroupingGroupingTest is incorrect", 2, groupingGroupingTest + assertEquals("Number of method in GroupingGroupingTest is incorrect", 3, groupingGroupingTest .getMethodDefinitions().size()); - assertEquals("Number of method in GroupingTest is incorrect", 2, groupingTest.getMethodDefinitions().size()); + assertEquals("Number of method in GroupingTest is incorrect", 3, groupingTest.getMethodDefinitions().size()); - containsMethods(groupingGroupingTest.getMethodDefinitions(), new NameTypePattern("getLeafGroupingGrouping", - "String")); + containsMethods(groupingGroupingTest.getMethodDefinitions(), + new NameTypePattern("getLeafGroupingGrouping", "String"), + new NameTypePattern("requireLeafGroupingGrouping", "String")); - containsMethods(groupingTest.getMethodDefinitions(), new NameTypePattern("getLeafGroupingTest", "Byte")); + containsMethods(groupingTest.getMethodDefinitions(), + new NameTypePattern("getLeafGroupingTest", "Byte"), + new NameTypePattern("requireLeafGroupingTest", "Byte")); } @Test @@ -261,19 +269,22 @@ public class UsesTest { containsInterface("GroupingListTest", listTest); - assertEquals("Number of method in GroupingListTest is incorrect", 6, groupingListTest.getMethodDefinitions() + assertEquals("Number of method in GroupingListTest is incorrect", 8, groupingListTest.getMethodDefinitions() .size()); // FIXME: split this into getter/default/static asserts - assertEquals(5, listTest.getMethodDefinitions().size()); + assertEquals(6, listTest.getMethodDefinitions().size()); // FIXME: split this into getter/default/static asserts - assertEquals(5, containerGroupingListTest.getMethodDefinitions().size()); + assertEquals(6, containerGroupingListTest.getMethodDefinitions().size()); // FIXME: split this into getter/default/static asserts - assertEquals(5, listGroupingListTest.getMethodDefinitions().size()); - - containsMethods(groupingListTest.getMethodDefinitions(), new NameTypePattern("getContainerGroupingListTest", - "ContainerGroupingListTest"), new NameTypePattern("getLeafGroupingListTest", "String"), - new NameTypePattern("getLeaffllistGroupingListTest", "List"), new NameTypePattern( - "getListGroupingListTest", "List")); + assertEquals(6, listGroupingListTest.getMethodDefinitions().size()); + + containsMethods(groupingListTest.getMethodDefinitions(), + new NameTypePattern("getContainerGroupingListTest", "ContainerGroupingListTest"), + new NameTypePattern("getLeafGroupingListTest", "String"), + new NameTypePattern("requireLeafGroupingListTest", "String"), + new NameTypePattern("getLeaffllistGroupingListTest", "List"), + new NameTypePattern("requireLeaffllistGroupingListTest", "List"), + new NameTypePattern("getListGroupingListTest", "List")); containsMethods(listTest.getMethodDefinitions(), new NameTypePattern("getListLeafTest", "String")); containsMethods(containerGroupingListTest.getMethodDefinitions(), new NameTypePattern( "getLeafContainerGroupingListTest", "Uint8")); @@ -319,11 +330,14 @@ public class UsesTest { assertEquals("Number of method in GroupingUsesModulData is incorrect", 0, groupingUsesModulData .getMethodDefinitions().size()); - assertEquals("Number of method in GroupingModulTest is incorrect", 3, groupingModulTest.getMethodDefinitions() + assertEquals("Number of method in GroupingModulTest is incorrect", 5, groupingModulTest.getMethodDefinitions() .size()); - containsMethods(groupingModulTest.getMethodDefinitions(), new NameTypePattern("getLeafGroupingModulTest", - "String"), new NameTypePattern("getLeafGroupingModulTest2", "Uint8")); + containsMethods(groupingModulTest.getMethodDefinitions(), + new NameTypePattern("getLeafGroupingModulTest", "String"), + new NameTypePattern("requireLeafGroupingModulTest", "String"), + new NameTypePattern("getLeafGroupingModulTest2", "Uint8"), + new NameTypePattern("requireLeafGroupingModulTest2", "Uint8")); } @Test @@ -399,20 +413,21 @@ public class UsesTest { assertEquals(4, rpcTestInput.getMethodDefinitions().size()); // FIXME: split this into getter/default/static asserts assertEquals(4, rpcTestOutput.getMethodDefinitions().size()); - assertEquals("Number of method in GroupingRpcInputTest is incorrect", 3, groupingRpcInputTest + assertEquals("Number of method in GroupingRpcInputTest is incorrect", 4, groupingRpcInputTest .getMethodDefinitions().size()); - assertEquals("Number of method in GroupingRpcOutputTest is incorrect", 2, groupingRpcOutputTest + assertEquals("Number of method in GroupingRpcOutputTest is incorrect", 3, groupingRpcOutputTest .getMethodDefinitions().size()); // FIXME: split this into getter/default/static asserts - assertEquals(5, containerGroupingRpcInputTest.getMethodDefinitions().size()); - - containsMethods(groupingRpcInputTest.getMethodDefinitions(), new NameTypePattern( - "getContainerGroupingRpcInputTest", "ContainerGroupingRpcInputTest"), new NameTypePattern( - "getLeaflistGroupingRpcInputTest", "List")); - containsMethods(groupingRpcOutputTest.getMethodDefinitions(), new NameTypePattern( - "getLeafGroupingRpcOutputTest", "Byte")); - containsMethods(containerGroupingRpcInputTest.getMethodDefinitions(), new NameTypePattern( - "getLeafContainerGroupingRpcInputTest", "String")); + assertEquals(6, containerGroupingRpcInputTest.getMethodDefinitions().size()); + + containsMethods(groupingRpcInputTest.getMethodDefinitions(), + new NameTypePattern("getContainerGroupingRpcInputTest", "ContainerGroupingRpcInputTest"), + new NameTypePattern("getLeaflistGroupingRpcInputTest", "List"), + new NameTypePattern("requireLeaflistGroupingRpcInputTest", "List")); + containsMethods(groupingRpcOutputTest.getMethodDefinitions(), + new NameTypePattern("getLeafGroupingRpcOutputTest", "Byte")); + containsMethods(containerGroupingRpcInputTest.getMethodDefinitions(), + new NameTypePattern("getLeafContainerGroupingRpcInputTest", "String")); } @Test @@ -454,11 +469,12 @@ public class UsesTest { assertEquals(4, containerAugment1.getMethodDefinitions().size()); // FIXME: split this into getter/default/static asserts assertEquals(4, containerAugment1.getMethodDefinitions().size()); - assertEquals("Number of method in GroupingCaseTest is incorrect", 2, groupingAugmentTest.getMethodDefinitions() + assertEquals("Number of method in GroupingCaseTest is incorrect", 3, groupingAugmentTest.getMethodDefinitions() .size()); - containsMethods(groupingAugmentTest.getMethodDefinitions(), new NameTypePattern("getLeafGroupingAugmentTest", - "String")); + containsMethods(groupingAugmentTest.getMethodDefinitions(), + new NameTypePattern("getLeafGroupingAugmentTest", "String"), + new NameTypePattern("requireLeafGroupingAugmentTest", "String")); } @Test @@ -513,19 +529,21 @@ public class UsesTest { containsInterface("GroupingNotificationTest", notificationTest); // FIXME: split this into getter/default/static asserts - assertEquals(5, notificationTest.getMethodDefinitions().size()); - assertEquals("Number of method in GroupingNotificationTest is incorrect", 3, groupingNotificationTest + assertEquals(6, notificationTest.getMethodDefinitions().size()); + assertEquals("Number of method in GroupingNotificationTest is incorrect", 4, groupingNotificationTest .getMethodDefinitions().size()); // FIXME: split this into getter/default/static asserts - assertEquals(5, containerGroupingNotificationTest.getMethodDefinitions().size()); - - containsMethods(notificationTest.getMethodDefinitions(), new NameTypePattern("getLeafNotificationTest", - "String")); - containsMethods(groupingNotificationTest.getMethodDefinitions(), new NameTypePattern( - "getContainerGroupingNotificationTest", "ContainerGroupingNotificationTest"), new NameTypePattern( - "getLeaffllistGroupingNotificationTest", "List")); - containsMethods(containerGroupingNotificationTest.getMethodDefinitions(), new NameTypePattern( - "getLeafContainerGroupingNotificationTest", "Uint32")); + assertEquals(6, containerGroupingNotificationTest.getMethodDefinitions().size()); + + containsMethods(notificationTest.getMethodDefinitions(), + new NameTypePattern("getLeafNotificationTest", "String")); + containsMethods(groupingNotificationTest.getMethodDefinitions(), + new NameTypePattern("getContainerGroupingNotificationTest", "ContainerGroupingNotificationTest"), + new NameTypePattern("getLeaffllistGroupingNotificationTest", "List"), + new NameTypePattern("requireLeaffllistGroupingNotificationTest", "List")); + containsMethods(containerGroupingNotificationTest.getMethodDefinitions(), + new NameTypePattern("getLeafContainerGroupingNotificationTest", "Uint32"), + new NameTypePattern("requireLeafContainerGroupingNotificationTest", "Uint32")); } } diff --git a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/InterfaceTemplate.xtend b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/InterfaceTemplate.xtend index c2eb8ca20c..90c0f336f1 100644 --- a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/InterfaceTemplate.xtend +++ b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/InterfaceTemplate.xtend @@ -8,10 +8,13 @@ package org.opendaylight.mdsal.binding.java.api.generator import static extension org.opendaylight.mdsal.binding.spec.naming.BindingMapping.getGetterMethodForNonnull +import static extension org.opendaylight.mdsal.binding.spec.naming.BindingMapping.getGetterMethodForRequire import static extension org.opendaylight.mdsal.binding.spec.naming.BindingMapping.isGetterMethodName import static extension org.opendaylight.mdsal.binding.spec.naming.BindingMapping.isNonnullMethodName +import static extension org.opendaylight.mdsal.binding.spec.naming.BindingMapping.isRequireMethodName import static org.opendaylight.mdsal.binding.model.util.Types.BOOLEAN import static org.opendaylight.mdsal.binding.model.util.Types.STRING +import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.REQUIRE_PREFIX import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.BINDING_EQUALS_NAME import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.BINDING_HASHCODE_NAME @@ -21,6 +24,7 @@ import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.DATA_CON import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects import java.util.List +import java.util.Locale import java.util.Map.Entry import java.util.Set import org.gaul.modernizer_maven_annotations.SuppressModernizer @@ -28,6 +32,7 @@ import org.opendaylight.mdsal.binding.model.api.AnnotationType import org.opendaylight.mdsal.binding.model.api.Constant import org.opendaylight.mdsal.binding.model.api.Enumeration import org.opendaylight.mdsal.binding.model.api.GeneratedType +import org.opendaylight.mdsal.binding.model.api.JavaTypeName import org.opendaylight.mdsal.binding.model.api.MethodSignature import org.opendaylight.mdsal.binding.model.api.Type import org.opendaylight.mdsal.binding.model.util.Types @@ -199,6 +204,8 @@ class InterfaceTemplate extends BaseTemplate { def private generateDefaultMethod(MethodSignature method) { if (method.name.isNonnullMethodName) { generateNonnullMethod(method) + } else if (method.name.isRequireMethodName) { + generateRequireMethod(method) } else { switch method.name { case DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME : generateDefaultImplementedInterface @@ -233,20 +240,27 @@ class InterfaceTemplate extends BaseTemplate { ''' def private accessorJavadoc(MethodSignature method, String orString) { - val reference = method.comment?.referenceDescription - val propReturn = method.propertyNameFromGetter + ", or " + orString + " if it is not present." + accessorJavadoc(method, orString, null) + } + + def private accessorJavadoc(MethodSignature method, String orString, JavaTypeName exception) { + val propName = method.propertyNameFromGetter + val propReturn = propName + orString return wrapToDocumentation(''' Return «propReturn» - «reference.formatReference» + «method.comment?.referenceDescription.formatReference» @return {@code «method.returnType.importedName»} «propReturn» + «IF exception !== null» + @throws «exception.importedName» if «propName» is not present + «ENDIF» ''') } def private generateAccessorMethod(MethodSignature method) { return ''' - «accessorJavadoc(method, "{@code null}")» + «accessorJavadoc(method, ", or {@code null} if it is not present.")» «method.generateAccessorAnnotations» «method.returnType.nullableType» «method.name»(); ''' @@ -343,13 +357,23 @@ class InterfaceTemplate extends BaseTemplate { def private generateNonnullMethod(MethodSignature method) ''' «val ret = method.returnType» «val name = method.name» - «accessorJavadoc(method, "an empty list")» + «accessorJavadoc(method, ", or an empty list if it is not present.")» «method.annotations.generateAnnotations» default «ret.importedNonNull» «name»() { return «CODEHELPERS.importedName».nonnull(«name.getGetterMethodForNonnull»()); } ''' + def private generateRequireMethod(MethodSignature method) ''' + «val ret = method.returnType» + «val name = method.name» + «val fieldName = name.toLowerCase(Locale.ROOT).replace(REQUIRE_PREFIX, "")» + «accessorJavadoc(method, ", guaranteed to be non-null.", NSEE)» + default «ret.importedNonNull» «name»() { + return «CODEHELPERS.importedName».require(«getGetterMethodForRequire(name)»(), "«fieldName»"); + } + ''' + def private String nullableType(Type type) { if (type.isObject && (Types.isMapType(type) || Types.isListType(type))) { return type.importedNullable diff --git a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/JavaFileTemplate.java b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/JavaFileTemplate.java index c096d1cf70..e56923324a 100644 --- a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/JavaFileTemplate.java +++ b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/JavaFileTemplate.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -64,6 +65,10 @@ class JavaFileTemplate { * {@code java.lang.NullPointerException} as a JavaTypeName. */ static final @NonNull JavaTypeName NPE = JavaTypeName.create(NullPointerException.class); + /** + * {@code java.lang.NoSuchElementException} as a JavaTypeName. + */ + static final @NonNull JavaTypeName NSEE = JavaTypeName.create(NoSuchElementException.class); /** * {@code java.lang.Override} as a JavaTypeName. */ @@ -344,6 +349,8 @@ class JavaFileTemplate { prefix = BindingMapping.GETTER_PREFIX; } else if (BindingMapping.isNonnullMethodName(getterName)) { prefix = BindingMapping.NONNULL_PREFIX; + } else if (BindingMapping.isRequireMethodName(getterName)) { + prefix = BindingMapping.REQUIRE_PREFIX; } else { throw new IllegalArgumentException(getterName + " is not a getter"); } diff --git a/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/CompilationTest.java b/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/CompilationTest.java index 68db74f46c..d0a0a914e3 100644 --- a/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/CompilationTest.java +++ b/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/CompilationTest.java @@ -119,6 +119,10 @@ public class CompilationTest extends BaseCompilationTest { assertTrue(linksClass.isInterface()); CompilationTestUtils.assertImplementsIfc(linksClass, keyArgsClass); assertEquals(7, abstractMethods(linksClass).size()); + CompilationTestUtils.assertContainsMethod(linksClass, + "org.opendaylight.yang.gen.v1.urn.opendaylight.test.rev131008.links.Text", "getText", loader); + CompilationTestUtils.assertContainsMethod(linksClass, + "org.opendaylight.yang.gen.v1.urn.opendaylight.test.rev131008.links.Text", "requireText", loader); // Test list key constructor arguments ordering CompilationTestUtils.assertContainsConstructor(linksKeyClass, Byte.class, String.class, Integer.class); diff --git a/binding/mdsal-binding-spec-util/src/main/java/org/opendaylight/mdsal/binding/spec/naming/BindingMapping.java b/binding/mdsal-binding-spec-util/src/main/java/org/opendaylight/mdsal/binding/spec/naming/BindingMapping.java index 75b3430c60..dd53f3d2d2 100644 --- a/binding/mdsal-binding-spec-util/src/main/java/org/opendaylight/mdsal/binding/spec/naming/BindingMapping.java +++ b/binding/mdsal-binding-spec-util/src/main/java/org/opendaylight/mdsal/binding/spec/naming/BindingMapping.java @@ -123,6 +123,11 @@ public final class BindingMapping { */ public static final @NonNull String NONNULL_PREFIX = "nonnull"; + /** + * Prefix for require default wrapper methods. These methods always wrap a corresponding normal getter + * of leaf objects. + */ + public static final @NonNull String REQUIRE_PREFIX = "require"; public static final @NonNull String RPC_INPUT_SUFFIX = "Input"; public static final @NonNull String RPC_OUTPUT_SUFFIX = "Output"; @@ -246,6 +251,19 @@ public final class BindingMapping { return methodName.startsWith(NONNULL_PREFIX); } + public static @NonNull String getGetterMethodForRequire(final String methodName) { + checkArgument(isRequireMethodName(methodName)); + return GETTER_PREFIX + methodName.substring(REQUIRE_PREFIX.length()); + } + + public static @NonNull String getRequireMethodName(final String localName) { + return REQUIRE_PREFIX + toFirstUpper(getPropertyName(localName)); + } + + public static boolean isRequireMethodName(final String methodName) { + return methodName.startsWith(REQUIRE_PREFIX); + } + public static @NonNull String getGetterSuffix(final QName name) { final String candidate = toFirstUpper(toCamelCase(name.getLocalName())); return "Class".equals(candidate) ? "XmlClass" : candidate; diff --git a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/CodeHelpers.java b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/CodeHelpers.java index bf3afc2945..cbdec28633 100644 --- a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/CodeHelpers.java +++ b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/CodeHelpers.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableMap; import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNull; @@ -47,6 +48,22 @@ public final class CodeHelpers { checkArgument(expression, "expected one of: %s \n%but was: %s", options, value); } + /** + * Return value and check whether specified value is null and if so throws exception. This method supports + * require default getter methods. + * + * @param value Value itself + * @param name Name of the value + * @return Non-null value + * @throws NoSuchElementException if value is null + */ + public static @NonNull T require(final @Nullable T value, final @NonNull String name) { + if (value == null) { + throw new NoSuchElementException("Value of " + name + " is not present"); + } + return value; + } + /** * A shortcut for {@code Preconditions.checkNotNull(value, "Key component \"%s\" must not be null", name)}. * -- 2.36.6