Add non-null getters for leaf objects 32/93932/17
authorKonstantin.Nosach <Kostiantyn.Nosach@pantheon.tech>
Wed, 25 Nov 2020 12:07:32 +0000 (14:07 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 23 Jun 2021 18:47:07 +0000 (20:47 +0200)
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 <kostiantyn.nosach@pantheon.tech>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
15 files changed:
binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractExplicitGenerator.java
binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractTypeAwareGenerator.java
binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/ListGenerator.java
binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/OpaqueObjectGenerator.java
binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/AugmentRelativeXPathTest.java
binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/GenEnumResolvingTest.java
binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/GeneratedTypesTest.java
binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/IdentityrefTypeTest.java
binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal320Test.java
binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/UsesTest.java
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/InterfaceTemplate.xtend
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/JavaFileTemplate.java
binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/CompilationTest.java
binding/mdsal-binding-spec-util/src/main/java/org/opendaylight/mdsal/binding/spec/naming/BindingMapping.java
binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/CodeHelpers.java

index 0ee1c8b2c9e2d539c3f84a4bd85d882acb9d9d1e..9a59a88f1459c671ec8d6e8e4773b9050b4b320b 100644 (file)
@@ -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<T extends EffectiveStatement<?,
             return;
         }
 
-        constructGetter(builder, methodReturnType(builderFactory));
+        final Type returnType = methodReturnType(builderFactory);
+        constructGetter(builder, returnType);
+        constructRequire(builder, returnType);
     }
 
     MethodSignatureBuilder constructGetter(final GeneratedTypeBuilderBase<?> 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<T extends EffectiveStatement<?,
         return getMethod;
     }
 
+    void constructRequire(final GeneratedTypeBuilderBase<?> 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
index 3579197ae36e00a4de060ffdd4f8f38c10b9e323..16a1c1fbc9f21b6d938e1bc31c486b08f250bf73 100644 (file)
@@ -84,4 +84,9 @@ abstract class AbstractTypeAwareGenerator<T extends DataTreeEffectiveStatement<?
 
         return ret;
     }
+
+    @Override
+    void constructRequire(final GeneratedTypeBuilderBase<?> builder, final Type returnType) {
+        constructRequireImpl(builder, returnType);
+    }
 }
index 890621c21617401330fa29077cf952547cd4b199..9138f0bd95995226b92323eb3cda59bed76fe8fe 100644 (file)
@@ -107,4 +107,9 @@ final class ListGenerator extends AbstractCompositeGenerator<ListEffectiveStatem
 
         return ret;
     }
+
+    @Override
+    void constructRequire(final GeneratedTypeBuilderBase<?> builder, final Type returnType) {
+        // No-op
+    }
 }
index 8a68246f999b7b1446c4f1476100ae9da4381ae1..da474e2cc4dad60753eb5e48703165f67af1c363 100644 (file)
@@ -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<T extends DataTreeEffectiveStatement<?>> exten
 
         return builder.build();
     }
+
+    @Override
+    void constructRequire(final GeneratedTypeBuilderBase<?> builder, final Type returnType) {
+        constructRequireImpl(builder, returnType);
+    }
 }
index 7502807e32589446ce7f693b8a4214c34e218799..9c2c03e7df717ce930cddb255495a3a4efe45a7e 100644 (file)
@@ -56,7 +56,7 @@ public class AugmentRelativeXPathTest {
 
                 final List<MethodSignature> 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<MethodSignature> tunnelMethods = gtTunnel.getMethodDefinitions();
                 assertNotNull("Tunnel methods are null", tunnelMethods);
-                assertEquals(6, tunnelMethods.size());
+                assertEquals(7, tunnelMethods.size());
 
                 MethodSignature getTunnelKeyMethod = null;
                 for (MethodSignature method : tunnelMethods) {
index ec76970eede18ae93431b7e6b610fd0ba1a5914e..397165370b76ae100e0c1647792a51f720ba46a5 100644 (file)
@@ -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();
index caf1d27a5dd7f335a68260c56af553bdf2801515..9a268e471d35064567bab092212e7a352eec52a7 100644 (file)
@@ -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
index 8f51e8ae4b7bf593658a46afc86d11ce914cbee6..3a9e063078f75037bac24fbf7601100e5332b99a 100644 (file)
@@ -62,10 +62,11 @@ public class IdentityrefTypeTest {
             .orElseThrow();
 
         List<MethodSignature> 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));
index 10c909f6583403b1a504688b932b11efff98eadc..d5009b4e422a016a3b13894b6569aede016fb4d4 100644 (file)
@@ -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();
index 621a3b3f1c99bdb1201c82c41ba4147bc6a0dfec..30596c02eaabae1de0df0014367171b05d804f16 100644 (file)
@@ -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<String>"), new NameTypePattern(
-                        "getListGroupingListTest", "List<ListGroupingListTest>"));
+        assertEquals(6, listGroupingListTest.getMethodDefinitions().size());
+
+        containsMethods(groupingListTest.getMethodDefinitions(),
+            new NameTypePattern("getContainerGroupingListTest", "ContainerGroupingListTest"),
+            new NameTypePattern("getLeafGroupingListTest", "String"),
+            new NameTypePattern("requireLeafGroupingListTest", "String"),
+            new NameTypePattern("getLeaffllistGroupingListTest", "List<String>"),
+            new NameTypePattern("requireLeaffllistGroupingListTest", "List<String>"),
+            new NameTypePattern("getListGroupingListTest", "List<ListGroupingListTest>"));
         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<Uint8>"));
-        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<Uint8>"),
+            new NameTypePattern("requireLeaflistGroupingRpcInputTest", "List<Uint8>"));
+        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<String>"));
-        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<String>"),
+            new NameTypePattern("requireLeaffllistGroupingNotificationTest", "List<String>"));
+        containsMethods(containerGroupingNotificationTest.getMethodDefinitions(),
+            new NameTypePattern("getLeafContainerGroupingNotificationTest", "Uint32"),
+            new NameTypePattern("requireLeafContainerGroupingNotificationTest", "Uint32"));
     }
 
 }
index c2eb8ca20c954685391b639376c71f2d9230152c..90c0f336f12bb3f923e36e094c4864a1243792e8 100644 (file)
@@ -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
index c096d1cf708a776b0dccba495475f3e5b8f79eb7..e56923324a55218dca89b2ba0e1ca500aef46e3d 100644 (file)
@@ -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");
         }
index 68db74f46ce24ec7f671af525f192dad5e7d5cb4..d0a0a914e39a73a0b0f392b3c054234bf880176a 100644 (file)
@@ -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);
index 75b3430c60a9ce21bdff56b373d37a6be6cbba32..dd53f3d2d26a4140aa303dd852c9192d856ba132 100644 (file)
@@ -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;
index bf3afc2945fa9f34743bbdc24d98801ecc51ca45..cbdec28633bcb821483ff24f09c0038695c493fd 100644 (file)
@@ -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 <T> @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)}.
      *