Generate bindingToString() and use it in generated implementations 48/90748/21
authorIlya Igushev <illia.ihushev@pantheon.tech>
Thu, 9 Jul 2020 09:52:02 +0000 (09:52 +0000)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 19 Jul 2020 19:59:46 +0000 (21:59 +0200)
As we are gearing towards generating default methods in interfaces,
we need the ability to completely analyze all a target type from
a template, not from a generator.

This ability was previously available to Builder*Template, now it
is part of JavaFileTemplate and hence can be picked up by
InterfaceTemplate as needed.

JIRA: MDSAL-479
Change-Id: I1e939aca15f2f77b82a83ca454503522c96bcabe
Signed-off-by: illia.ihushev <illia.ihushev@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/AbstractTypeGenerator.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/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/AbstractBuilderTemplate.xtend
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BaseTemplate.xtend
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderImplTemplate.xtend
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderTemplate.xtend
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/ClassTemplate.xtend
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/BuilderGeneratorTest.java
binding/mdsal-binding-spec-util/src/main/java/org/opendaylight/mdsal/binding/spec/naming/BindingMapping.java
binding/mdsal-binding-spec-util/src/main/java/org/opendaylight/mdsal/binding/spec/reflect/BindingReflections.java

index 036a192b767690f38235064462470d9a43df37ba..ff1197b8e8c0239ade7785de4b68adda8892a328 100644 (file)
@@ -291,7 +291,7 @@ abstract class AbstractTypeGenerator {
             return null;
         }
         final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(context, node, baseInterface);
-        defaultImplementedInterace(genType);
+        addConcreteInterfaceMethods(genType);
         annotateDeprecatedIfNecessary(node, genType);
 
         final Module module = context.module();
@@ -571,7 +571,7 @@ abstract class AbstractTypeGenerator {
         addImplementedInterfaceFromUses(schema, outType);
         outType.addImplementsType(type);
         outType.addImplementsType(augmentable(outType));
-        defaultImplementedInterace(outType);
+        addConcreteInterfaceMethods(outType);
         annotateDeprecatedIfNecessary(rpc, outType);
         resolveDataSchemaNodes(context, outType, outType, schema.getChildNodes(), false);
         context.addChildNodeType(schema, outType);
@@ -611,7 +611,7 @@ abstract class AbstractTypeGenerator {
 
                 final GeneratedTypeBuilder notificationInterface = addDefaultInterfaceDefinition(
                     context.modulePackageName(), notification, DATA_OBJECT, context);
-                defaultImplementedInterace(notificationInterface);
+                addConcreteInterfaceMethods(notificationInterface);
                 annotateDeprecatedIfNecessary(notification, notificationInterface);
                 notificationInterface.addImplementsType(NOTIFICATION);
                 context.addChildNodeType(notification, notificationInterface);
@@ -652,7 +652,7 @@ abstract class AbstractTypeGenerator {
 
             final GeneratedTypeBuilder notifInterface = addDefaultInterfaceDefinition(
                 packageNameForGeneratedType(context.modulePackageName(), notif.getPath()), notif, DATA_OBJECT, context);
-            defaultImplementedInterace(notifInterface);
+            addConcreteInterfaceMethods(notifInterface);
             annotateDeprecatedIfNecessary(notif, notifInterface);
 
             notifInterface.addImplementsType(keyType != null ? keyedListNotification(notifInterface, parent, keyType)
@@ -1020,7 +1020,7 @@ abstract class AbstractTypeGenerator {
             JavaTypeName.create(augmentPackageName, augTypeName));
 
         augTypeBuilder.addImplementsType(augmentation(targetTypeRef));
-        defaultImplementedInterace(augTypeBuilder);
+        addConcreteInterfaceMethods(augTypeBuilder);
 
         annotateDeprecatedIfNecessary(augSchema, augTypeBuilder);
         addImplementedInterfaceFromUses(augSchema, augTypeBuilder);
@@ -1233,7 +1233,7 @@ abstract class AbstractTypeGenerator {
             if (caseNode != null && !caseNode.isAddedByUses() && !caseNode.isAugmenting()) {
                 final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(context, caseNode);
                 caseTypeBuilder.addImplementsType(refChoiceType);
-                defaultImplementedInterace(caseTypeBuilder);
+                addConcreteInterfaceMethods(caseTypeBuilder);
                 annotateDeprecatedIfNecessary(caseNode, caseTypeBuilder);
                 context.addCaseType(caseNode.getPath(), caseTypeBuilder);
                 context.addChoiceToCaseMapping(refChoiceType, caseTypeBuilder, caseNode);
@@ -1313,7 +1313,7 @@ abstract class AbstractTypeGenerator {
             if (caseNode != null) {
                 final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(context, caseNode);
                 caseTypeBuilder.addImplementsType(targetType);
-                defaultImplementedInterace(caseTypeBuilder);
+                addConcreteInterfaceMethods(caseTypeBuilder);
 
                 CaseSchemaNode node = null;
                 final String caseLocalName = caseNode.getQName().getLocalName();
@@ -2056,6 +2056,15 @@ abstract class AbstractTypeGenerator {
         }
     }
 
+    private static void addConcreteInterfaceMethods(final GeneratedTypeBuilder typeBuilder) {
+        defaultImplementedInterace(typeBuilder);
+
+        typeBuilder.addMethod(BindingMapping.BINDING_TO_STRING_NAME)
+            .setAccessModifier(AccessModifier.PUBLIC)
+            .setStatic(true)
+            .setReturnType(STRING);
+    }
+
     private static void narrowImplementedInterface(final GeneratedTypeBuilder typeBuilder) {
         defineImplementedInterfaceMethod(typeBuilder, wildcardTypeFor(typeBuilder.getIdentifier()));
     }
index ae0b74ea7c7d29c23851228696c83961872c0882..6869fd6922af2cfb0d6c02e4188c9d174730d468 100644 (file)
@@ -65,7 +65,8 @@ public class GenEnumResolvingTest {
 
         assertNotNull("Generated Interface cannot contain NULL reference for Method Signature Definitions!", methods);
 
-        assertEquals("Expected count of method signature definitions is 16", 16, methods.size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(17, methods.size());
         Enumeration ianaIfType = null;
         for (final MethodSignature method : methods) {
             if (method.getName().equals("getType")) {
@@ -118,7 +119,9 @@ public class GenEnumResolvingTest {
         Type operStatus = null;
         final List<MethodSignature> methods = genInterface.getMethodDefinitions();
         assertNotNull("Generated Type Interface cannot contain NULL reference to Enumeration types!", methods);
-        assertEquals("Generated Type Interface MUST contain 6 Methods ", 6, methods.size());
+
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(7, methods.size());
         for (final MethodSignature method : methods) {
             if (method.getName().equals("getLinkUpDownTrapEnable")) {
                 linkUpDownTrapEnable = method.getReturnType();
index 3fc45f4c95a5868d3f2cf3e2218adb92a71b4bd8..3f97c93154f698daea450c1ec0f04bbf5640a8bb 100644 (file)
@@ -50,8 +50,10 @@ public class GeneratedTypesTest {
         }
         assertNotNull(simpleContainer);
         assertNotNull(nestedContainer);
-        assertEquals(4, simpleContainer.getMethodDefinitions().size());
-        assertEquals(3, nestedContainer.getMethodDefinitions().size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(5, simpleContainer.getMethodDefinitions().size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(4, nestedContainer.getMethodDefinitions().size());
 
         int getFooMethodCounter = 0;
         int getBarMethodCounter = 0;
@@ -131,8 +133,10 @@ public class GeneratedTypesTest {
         }
         assertNotNull(simpleContainer);
         assertNotNull(nestedContainer);
-        assertEquals(4, simpleContainer.getMethodDefinitions().size());
-        assertEquals(3, nestedContainer.getMethodDefinitions().size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(5, simpleContainer.getMethodDefinitions().size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(4, nestedContainer.getMethodDefinitions().size());
 
         int getFooMethodCounter = 0;
         int getBarMethodCounter = 0;
@@ -294,8 +298,10 @@ public class GeneratedTypesTest {
             }
         }
 
-        assertEquals(3, listParentContainerMethodsCount);
-        assertEquals(2, listChildContainerMethodsCount);
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(4, listParentContainerMethodsCount);
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(3, listChildContainerMethodsCount);
         assertEquals(1, getSimpleListKeyMethodCount);
         assertEquals(1, listKeyClassCount);
 
@@ -320,7 +326,8 @@ public class GeneratedTypesTest {
         assertEquals(0, setSimpleLeafListMethodCount);
         assertEquals(1, getBarMethodCount);
 
-        assertEquals(7, simpleListMethodsCount);
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(8, simpleListMethodsCount);
     }
 
     @Test
index ce7ea28b8d202c80b1c48586cc3e26611bef27c2..80546fd7a1da562878cad95513d76e7ef093ca01 100644 (file)
@@ -20,6 +20,7 @@ import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject;
 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.spec.naming.BindingMapping;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 
 public class Mdsal320Test {
@@ -63,6 +64,9 @@ public class Mdsal320Test {
         assertTrue(getImplIface.isDefault());
         assertTrue(it.hasNext());
 
+        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();
index 533a33c3c46c8b16948ee2e922c19747360d1f11..ee4be95b62f09898239b73c46d588843fd60bba9 100644 (file)
@@ -105,7 +105,8 @@ public class UsesTest {
                 "org.opendaylight.yang.gen.v1.urn.grouping.uses._case.rev130718", groupingCaseTest.getPackageName());
 
         containsInterface("GroupingCaseTest", caseC);
-        assertEquals("Case C shouldn't contain any method.", 1, caseC.getMethodDefinitions().size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(2, caseC.getMethodDefinitions().size());
 
         assertEquals("Number of method in GroupingCaseTest is incorrect", 2, groupingCaseTest.getMethodDefinitions()
                 .size());
@@ -152,7 +153,8 @@ public class UsesTest {
 
         assertEquals("Number of method in GroupingContainerTestis incorrect", 3, groupingContainerTest
                 .getMethodDefinitions().size());
-        assertEquals("Number of method in ContainerTest is incorrect", 2, containerTest.getMethodDefinitions().size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(3, containerTest.getMethodDefinitions().size());
 
         containsMethods(groupingContainerTest.getMethodDefinitions(), new NameTypePattern(
                 "getLeafGroupingContainerTest1", "String"), new NameTypePattern("getLeafGroupingContainerTest2",
@@ -267,11 +269,12 @@ public class UsesTest {
 
         assertEquals("Number of method in GroupingListTest is incorrect", 6, groupingListTest.getMethodDefinitions()
                 .size());
-        assertEquals("Number of method in ListTest is incorrect", 2, listTest.getMethodDefinitions().size());
-        assertEquals("Number of method in ContainerGroupingListTest is incorrect", 2, containerGroupingListTest
-                .getMethodDefinitions().size());
-        assertEquals("Number of method in ListGroupingListTest is incorrect", 2, listGroupingListTest
-                .getMethodDefinitions().size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(3, listTest.getMethodDefinitions().size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(3, containerGroupingListTest.getMethodDefinitions().size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(3, listGroupingListTest.getMethodDefinitions().size());
 
         containsMethods(groupingListTest.getMethodDefinitions(), new NameTypePattern("getContainerGroupingListTest",
                 "ContainerGroupingListTest"), new NameTypePattern("getLeafGroupingListTest", "String"),
@@ -400,14 +403,16 @@ public class UsesTest {
         containsInterface("GroupingRpcInputTest", rpcTestInput);
         containsInterface("GroupingRpcOutputTest", rpcTestOutput);
 
-        assertEquals("Number of method in RpcTestInput is incorrect", 1, rpcTestInput.getMethodDefinitions().size());
-        assertEquals("Number of method in RpcTestOutput is incorrect", 1, rpcTestOutput.getMethodDefinitions().size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(2, rpcTestInput.getMethodDefinitions().size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(2, rpcTestOutput.getMethodDefinitions().size());
         assertEquals("Number of method in GroupingRpcInputTest is incorrect", 3, groupingRpcInputTest
                 .getMethodDefinitions().size());
         assertEquals("Number of method in GroupingRpcOutputTest is incorrect", 2, groupingRpcOutputTest
                 .getMethodDefinitions().size());
-        assertEquals("Number of method in ContainerGroupingRpcInputTest is incorrect", 2, containerGroupingRpcInputTest
-                .getMethodDefinitions().size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(3, containerGroupingRpcInputTest.getMethodDefinitions().size());
 
         containsMethods(groupingRpcInputTest.getMethodDefinitions(), new NameTypePattern(
                 "getContainerGroupingRpcInputTest", "ContainerGroupingRpcInputTest"), new NameTypePattern(
@@ -454,11 +459,10 @@ public class UsesTest {
 
         containsInterface("GroupingAugmentTest", containerAugment1);
 
-        assertEquals("Number of method in GroupingCaseTest is incorrect", 1, containerAugment1.getMethodDefinitions()
-                .size());
-
-        assertEquals("Number of method in ContainerAugment1 is incorrect", 1, containerAugment1.getMethodDefinitions()
-                .size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(2, containerAugment1.getMethodDefinitions().size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(2, containerAugment1.getMethodDefinitions().size());
         assertEquals("Number of method in GroupingCaseTest is incorrect", 2, groupingAugmentTest.getMethodDefinitions()
                 .size());
 
@@ -517,12 +521,12 @@ public class UsesTest {
 
         containsInterface("GroupingNotificationTest", notificationTest);
 
-        assertEquals("Number of method in NotificationTest is incorrect", 2, notificationTest.getMethodDefinitions()
-                .size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(3, notificationTest.getMethodDefinitions().size());
         assertEquals("Number of method in GroupingNotificationTest is incorrect", 3, groupingNotificationTest
                 .getMethodDefinitions().size());
-        assertEquals("Number of method in ContainerGroupingNotificationTest is incorrect", 2,
-                containerGroupingNotificationTest.getMethodDefinitions().size());
+        // FIXME: split this into getter/default/static asserts
+        assertEquals(3, containerGroupingNotificationTest.getMethodDefinitions().size());
 
         containsMethods(notificationTest.getMethodDefinitions(), new NameTypePattern("getLeafNotificationTest",
                 "String"));
index fd34bf5cb5d98d9777e93dd28782797fc70a21b8..85147c869b8871e5162f2cbc710566d4bf32dcf0 100644 (file)
@@ -7,10 +7,8 @@
  */
 package org.opendaylight.mdsal.binding.java.api.generator
 
-import static org.opendaylight.mdsal.binding.model.util.Types.STRING;
 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD
 
-import com.google.common.base.MoreObjects
 import java.util.ArrayList
 import java.util.Collection
 import java.util.Collections
@@ -88,22 +86,6 @@ abstract class AbstractBuilderTemplate extends BaseTemplate {
         '''
     }
 
-    override generateToString(Collection<? extends GeneratedProperty> properties) '''
-        «IF properties !== null»
-            @«OVERRIDE.importedName»
-            public «STRING.importedName» toString() {
-                final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper("«targetType.name»");
-                «FOR property : properties»
-                    «CODEHELPERS.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
-                «ENDFOR»
-                «IF augmentType !== null»
-                    «CODEHELPERS.importedName».appendValue(helper, "«AUGMENTATION_FIELD»", augmentations().values());
-                «ENDIF»
-                return helper.toString();
-            }
-        «ENDIF»
-    '''
-
     /**
      * Template method which generate getter methods for IMPL class.
      *
index 92bd21dae47fd26d2ba9f28a1560659f94541636..63689ab1cd15a728bf6479bdd148e7d0c91cfb25 100644 (file)
@@ -8,10 +8,8 @@
 package org.opendaylight.mdsal.binding.java.api.generator
 
 import static org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil.encodeAngleBrackets
-import static org.opendaylight.mdsal.binding.model.util.Types.STRING;
 
 import com.google.common.base.CharMatcher
-import com.google.common.base.MoreObjects
 import com.google.common.base.Splitter
 import com.google.common.collect.ImmutableMap
 import com.google.common.collect.Iterables
@@ -414,19 +412,6 @@ abstract class BaseTemplate extends JavaFileTemplate {
         return sb.append(lineBuilder).append(NEW_LINE).toString
     }
 
-    def protected generateToString(Collection<? extends GeneratedProperty> properties) '''
-        «IF !properties.empty»
-            @«OVERRIDE.importedName»
-            public «STRING.importedName» toString() {
-                final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper(«type.importedName».class);
-                «FOR property : properties»
-                    «CODEHELPERS.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
-                «ENDFOR»
-                return helper.toString();
-            }
-        «ENDIF»
-    '''
-
     /**
      * Template method which generates method parameters with their types from <code>parameters</code>.
      *
index c4df75d62eb49d6bedd6d098ce9b24fef1b8911b..18a4d9ec04e93bb7035c5a3d93b60991aeadc19f 100644 (file)
@@ -8,8 +8,10 @@
 package org.opendaylight.mdsal.binding.java.api.generator
 
 import static org.opendaylight.mdsal.binding.model.util.BindingTypes.DATA_OBJECT
+import static org.opendaylight.mdsal.binding.model.util.Types.STRING;
 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD
 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTABLE_AUGMENTATION_NAME
+import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.BINDING_TO_STRING_NAME
 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME
 
 import java.util.Collection
@@ -51,7 +53,7 @@ class BuilderImplTemplate extends AbstractBuilderTemplate {
 
             «generateEquals()»
 
-            «generateToString(properties
+            «generateToString()»
         }
     '''
 
@@ -137,6 +139,18 @@ class BuilderImplTemplate extends AbstractBuilderTemplate {
         «ENDIF»
     '''
 
+    /**
+     * Template method which generates the method <code>toString()</code>.
+     *
+     * @return string with the <code>toString()</code> method definition in JAVA format
+     */
+    def protected generateToString() '''
+        @«OVERRIDE.importedName»
+        public «STRING.importedName» toString() {
+            return «targetType.importedName».«BINDING_TO_STRING_NAME»(this);
+        }
+    '''
+
     override protected generateCopyKeys(List<GeneratedProperty> keyProps) '''
         if (base.«BindingMapping.IDENTIFIABLE_KEY_NAME»() != null) {
             this.key = base.«BindingMapping.IDENTIFIABLE_KEY_NAME»();
index b1c37acd33ac91e1903492f4e7d9ea0416d80f82..450de7ccfa33b923926d7704e43dd6b2b4f44a9a 100644 (file)
@@ -31,7 +31,6 @@ import org.opendaylight.mdsal.binding.model.util.TypeConstants
 import org.opendaylight.mdsal.binding.model.util.Types
 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
 import org.opendaylight.yangtools.concepts.Builder
-import org.opendaylight.yangtools.yang.binding.AugmentationHolder
 
 /**
  * Template for generating JAVA builder classes.
@@ -500,7 +499,7 @@ class BuilderTemplate extends AbstractBuilderTemplate {
     '''
 
     override protected generateCopyAugmentation(Type implType) {
-        val augmentationHolderRef = AugmentationHolder.importedName
+        val augmentationHolderRef = AUGMENTATION_HOLDER.importedName
         val typeRef = targetType.importedName
         val hashMapRef = JU_HASHMAP.importedName
         val augmentTypeRef = augmentType.importedName
index 3b18d18815c34424358ee347cc25b915f4a5c9af..5c3ec4a824b60503d39ba808e5e870633e81f895 100644 (file)
@@ -26,6 +26,7 @@ import static org.opendaylight.mdsal.binding.model.util.Types.BOOLEAN
 import static org.opendaylight.mdsal.binding.model.util.Types.STRING;
 import static extension org.apache.commons.text.StringEscapeUtils.escapeJava
 
+import com.google.common.base.MoreObjects
 import com.google.common.base.Preconditions
 import com.google.common.collect.ImmutableList
 import com.google.common.collect.Lists
@@ -33,6 +34,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
 import java.beans.ConstructorProperties
 import java.util.ArrayList
 import java.util.Base64;
+import java.util.Collection
 import java.util.Comparator
 import java.util.List
 import java.util.Map
@@ -615,6 +617,19 @@ class ClassTemplate extends BaseTemplate {
         «ENDIF»
     '''
 
+    def private generateToString(Collection<? extends GeneratedProperty> properties) '''
+        «IF !properties.empty»
+            @«OVERRIDE.importedName»
+            public «STRING.importedName» toString() {
+                final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper(«type.importedName».class);
+                «FOR property : properties»
+                    «CODEHELPERS.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
+                «ENDFOR»
+                return helper.toString();
+            }
+        «ENDIF»
+    '''
+
     def GeneratedProperty getPropByName(String name) {
         for (GeneratedProperty prop : allProperties) {
             if (prop.name.equals(name)) {
index 3797ccba0263ff6c8d9d14ffd9c296c7a03b52f1..619d6c2073967362bf3a87d910118bd25448a167 100644 (file)
@@ -7,11 +7,15 @@
  */
 package org.opendaylight.mdsal.binding.java.api.generator
 
-import static extension org.opendaylight.mdsal.binding.spec.naming.BindingMapping.DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME
+import static org.opendaylight.mdsal.binding.model.util.Types.STRING;
 import static extension org.opendaylight.mdsal.binding.spec.naming.BindingMapping.getGetterMethodForNonnull
 import static extension org.opendaylight.mdsal.binding.spec.naming.BindingMapping.isGetterMethodName
 import static extension org.opendaylight.mdsal.binding.spec.naming.BindingMapping.isNonnullMethodName
+import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD
+import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.BINDING_TO_STRING_NAME
+import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME
 
+import com.google.common.base.MoreObjects
 import java.util.List
 import java.util.Map.Entry
 import java.util.Set
@@ -164,6 +168,8 @@ class InterfaceTemplate extends BaseTemplate {
             «FOR m : methods SEPARATOR "\n"»
                 «IF m.isDefault»
                     «generateDefaultMethod(m)»
+                «ELSEIF m.isStatic»
+                    «generateStaticMethod(m)»
                 «ELSEIF m.parameters.empty && m.name.isGetterMethodName»
                     «generateAccessorMethod(m)»
                 «ELSE»
@@ -183,6 +189,12 @@ class InterfaceTemplate extends BaseTemplate {
         }
     }
 
+    def private generateStaticMethod(MethodSignature method) {
+        switch method.name {
+            case BINDING_TO_STRING_NAME : generateBindingToString
+        }
+    }
+
     def private generateMethod(MethodSignature method) '''
         «method.comment.asJavadoc»
         «method.annotations.generateAnnotations»
@@ -203,6 +215,38 @@ class InterfaceTemplate extends BaseTemplate {
         }
     '''
 
+    def generateBindingToString() '''
+        «val augmentable = analyzeType»
+        /**
+         * Default implementation of {@link «Object.importedName»#toString()} contract for this interface.
+         * Implementations of this interface are encouraged to defer to this method to get consistent string
+         * representations across all implementation.
+         *
+         «IF augmentable»
+         * <p>
+         * @param <T$$> implementation type, which has to also implement «AUGMENTATION_HOLDER.importedName» interface
+         *              contract.
+         «ENDIF»
+         * @param obj Object for which to generate toString() result.
+         * @return {@link «STRING.importedName»} value of data modeled by this interface.
+         * @throws «NPE.importedName» if {@code obj} is null
+         */
+        «IF augmentable»
+            static <T$$ extends «type.fullyQualifiedName» & «AUGMENTATION_HOLDER.importedName»<«type.fullyQualifiedName»>> «STRING.importedName» «BINDING_TO_STRING_NAME»(final @«NONNULL.importedName» T$$ obj) {
+        «ELSE»
+            static «STRING.importedName» «BINDING_TO_STRING_NAME»(final «type.fullyQualifiedName» obj) {
+        «ENDIF»
+            final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper("«type.name»");
+            «FOR property : typeAnalysis.value»
+                «CODEHELPERS.importedName».appendValue(helper, "«property.name»", obj.«property.getterName»());
+            «ENDFOR»
+            «IF augmentable»
+                «CODEHELPERS.importedName».appendValue(helper, "«AUGMENTATION_FIELD»", obj.augmentations().values());
+            «ENDIF»
+            return helper.toString();
+        }
+    '''
+
     def private generateNonnullMethod(MethodSignature method) '''
         «val ret = method.returnType»
         «val name = method.name»
@@ -225,9 +269,10 @@ class InterfaceTemplate extends BaseTemplate {
         return !type.getPackageName().isEmpty()
     }
 
-    def analyzeType() {
+    private def boolean analyzeType() {
         if (typeAnalysis === null) {
             typeAnalysis = analyzeTypeHierarchy(type)
         }
+        typeAnalysis.key !== null
     }
 }
index 3698572784a4cb9d7e39927452a74a1fc5ed3871..3aa4f8f7901ba0a5cc998ebb7c4aba8072e3400b 100644 (file)
@@ -43,6 +43,7 @@ import org.opendaylight.mdsal.binding.model.api.Type;
 import org.opendaylight.mdsal.binding.model.util.Types;
 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.AugmentationHolder;
 import org.opendaylight.yangtools.yang.binding.CodeHelpers;
 
 
@@ -58,11 +59,14 @@ class JavaFileTemplate {
      * {@code java.lang.Deprecated} as a JavaTypeName.
      */
     static final @NonNull JavaTypeName DEPRECATED = JavaTypeName.create(Deprecated.class);
+    /**
+     * {@code java.lang.NullPointerException} as a JavaTypeName.
+     */
+    static final @NonNull JavaTypeName NPE = JavaTypeName.create(NullPointerException.class);
     /**
      * {@code java.lang.Override} as a JavaTypeName.
      */
     static final @NonNull JavaTypeName OVERRIDE = JavaTypeName.create(Override.class);
-
     /**
      * {@code java.lang.SuppressWarnings} as a JavaTypeName.
      */
@@ -106,6 +110,10 @@ class JavaFileTemplate {
      * {@code org.opendaylight.yangtools.yang.binding.CodeHelpers} as a JavaTypeName.
      */
     static final @NonNull JavaTypeName CODEHELPERS = JavaTypeName.create(CodeHelpers.class);
+    /**
+     * {@code org.opendaylight.yangtools.yang.binding.AugmentationHolder} as a JavaTypeName.
+     */
+    static final @NonNull JavaTypeName AUGMENTATION_HOLDER = JavaTypeName.create(AugmentationHolder.class);
 
     private static final Comparator<MethodSignature> METHOD_COMPARATOR = new AlphabeticallyTypeMemberComparator<>();
     private static final Type AUGMENTATION_RET_TYPE;
index 6602747408725f7ca202481991e8738b57b170cb..0e167acc3c0d1f65e78623ec0b864c9ad64928c9 100644 (file)
@@ -34,18 +34,34 @@ public class BuilderGeneratorTest {
     public void builderTemplateGenerateToStringWithPropertyTest() {
         final GeneratedType genType = mockGenType("get" + TEST);
 
-        assertEquals("@Override\n"
-                + "public String toString() {\n"
+        assertEquals("/**\n"
+                + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
+                + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
+                + "\n * representations across all implementation.\n"
+                + " *\n"
+                + " * @param obj Object for which to generate toString() result.\n"
+                + " * @return {@link String} value of data modeled by this interface.\n"
+                + " * @throws NullPointerException if {@code obj} is null\n"
+                + " */\n"
+                + "static String bindingToString(final test.test obj) {\n"
                 + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
-                + "    CodeHelpers.appendValue(helper, \"_test\", _test);\n"
+                + "    CodeHelpers.appendValue(helper, \"test\", obj.gettest());\n"
                 + "    return helper.toString();\n"
                 + "}\n", genToString(genType).toString());
     }
 
     @Test
     public void builderTemplateGenerateToStringWithoutAnyPropertyTest() throws Exception {
-        assertEquals("@Override\n"
-                + "public String toString() {\n"
+        assertEquals("/**\n"
+                + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
+                + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
+                + "\n * representations across all implementation.\n"
+                + " *\n"
+                + " * @param obj Object for which to generate toString() result.\n"
+                + " * @return {@link String} value of data modeled by this interface.\n"
+                + " * @throws NullPointerException if {@code obj} is null\n"
+                + " */\n"
+                + "static String bindingToString(final test.test obj) {\n"
                 + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
                 + "    return helper.toString();\n"
                 + "}\n", genToString(mockGenType(TEST)).toString());
@@ -53,44 +69,88 @@ public class BuilderGeneratorTest {
 
     @Test
     public void builderTemplateGenerateToStringWithMorePropertiesTest() throws Exception {
-        assertEquals("@Override\n"
-                + "public String toString() {\n"
+        assertEquals("/**\n"
+                + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
+                + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
+                + "\n * representations across all implementation.\n"
+                + " *\n"
+                + " * @param obj Object for which to generate toString() result.\n"
+                + " * @return {@link String} value of data modeled by this interface.\n"
+                + " * @throws NullPointerException if {@code obj} is null\n"
+                + " */\n"
+                + "static String bindingToString(final test.test obj) {\n"
                 + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
-                + "    CodeHelpers.appendValue(helper, \"_test1\", _test1);\n"
-                + "    CodeHelpers.appendValue(helper, \"_test2\", _test2);\n"
+                + "    CodeHelpers.appendValue(helper, \"test1\", obj.gettest1());\n"
+                + "    CodeHelpers.appendValue(helper, \"test2\", obj.gettest2());\n"
                 + "    return helper.toString();\n"
                 + "}\n", genToString(mockGenTypeMoreMeth("get" + TEST)).toString());
     }
 
     @Test
     public void builderTemplateGenerateToStringWithoutPropertyWithAugmentTest() throws Exception {
-        assertEquals("@Override\n"
-                + "public String toString() {\n"
+        assertEquals("/**\n"
+                + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
+                + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
+                + "\n * representations across all implementation.\n"
+                + " *\n"
+                + " * <p>\n"
+                + " * @param <T$$> implementation type, which has to also implement AugmentationHolder interface\n"
+                + " *              contract.\n"
+                + " * @param obj Object for which to generate toString() result.\n"
+                + " * @return {@link String} value of data modeled by this interface.\n"
+                + " * @throws NullPointerException if {@code obj} is null\n"
+                + " */\n"
+                + "static <T$$ extends test.test & AugmentationHolder<test.test>> String bindingToString(final @NonNull"
+                + " T$$ obj) {\n"
                 + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
-                + "    CodeHelpers.appendValue(helper, \"augmentation\", augmentations().values());\n"
+                + "    CodeHelpers.appendValue(helper, \"augmentation\", obj.augmentations().values());\n"
                 + "    return helper.toString();\n"
                 + "}\n", genToString(mockAugment(mockGenType(TEST))).toString());
     }
 
     @Test
     public void builderTemplateGenerateToStringWithPropertyWithAugmentTest() throws Exception {
-        assertEquals("@Override\n"
-                + "public String toString() {\n"
+        assertEquals("/**\n"
+                + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
+                + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
+                + "\n * representations across all implementation.\n"
+                + " *\n"
+                + " * <p>\n"
+                + " * @param <T$$> implementation type, which has to also implement AugmentationHolder interface\n"
+                + " *              contract.\n"
+                + " * @param obj Object for which to generate toString() result.\n"
+                + " * @return {@link String} value of data modeled by this interface.\n"
+                + " * @throws NullPointerException if {@code obj} is null\n"
+                + " */\n"
+                + "static <T$$ extends test.test & AugmentationHolder<test.test>> String bindingToString(final @NonNull"
+                + " T$$ obj) {\n"
                 + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
-                + "    CodeHelpers.appendValue(helper, \"_test\", _test);\n"
-                + "    CodeHelpers.appendValue(helper, \"augmentation\", augmentations().values());\n"
+                + "    CodeHelpers.appendValue(helper, \"test\", obj.gettest());\n"
+                + "    CodeHelpers.appendValue(helper, \"augmentation\", obj.augmentations().values());\n"
                 + "    return helper.toString();\n"
                 + "}\n", genToString(mockAugment(mockGenType("get" + TEST))).toString());
     }
 
     @Test
     public void builderTemplateGenerateToStringWithMorePropertiesWithAugmentTest() throws Exception {
-        assertEquals("@Override\n"
-                + "public String toString() {\n"
+        assertEquals("/**\n"
+                + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
+                + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
+                + "\n * representations across all implementation.\n"
+                + " *\n"
+                + " * <p>\n"
+                + " * @param <T$$> implementation type, which has to also implement AugmentationHolder interface\n"
+                + " *              contract.\n"
+                + " * @param obj Object for which to generate toString() result.\n"
+                + " * @return {@link String} value of data modeled by this interface.\n"
+                + " * @throws NullPointerException if {@code obj} is null\n"
+                + " */\n"
+                + "static <T$$ extends test.test & AugmentationHolder<test.test>> String bindingToString(final @NonNull"
+                + " T$$ obj) {\n"
                 + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
-                + "    CodeHelpers.appendValue(helper, \"_test1\", _test1);\n"
-                + "    CodeHelpers.appendValue(helper, \"_test2\", _test2);\n"
-                + "    CodeHelpers.appendValue(helper, \"augmentation\", augmentations().values());\n"
+                + "    CodeHelpers.appendValue(helper, \"test1\", obj.gettest1());\n"
+                + "    CodeHelpers.appendValue(helper, \"test2\", obj.gettest2());\n"
+                + "    CodeHelpers.appendValue(helper, \"augmentation\", obj.augmentations().values());\n"
                 + "    return helper.toString();\n"
                 + "}\n", genToString(mockAugment(mockGenTypeMoreMeth("get" + TEST))).toString());
     }
@@ -123,8 +183,7 @@ public class BuilderGeneratorTest {
     }
 
     private static CharSequence genToString(final GeneratedType genType) {
-        final BuilderTemplate bt = BuilderGenerator.templateForType(genType);
-        return bt.generateToString(bt.properties);
+        return new InterfaceTemplate(genType).generateBindingToString();
     }
 
     private static GeneratedType mockGenType(final String methodeName) {
index 7729ad194255f88a629ac7cfd2ceee9b21ef1e31..74908b796af6669b747a4b19f01cdd3c53a4285e 100644 (file)
@@ -80,6 +80,12 @@ public final class BindingMapping {
      */
     public static final @NonNull String DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME = "implementedInterface";
 
+    /**
+     * Name of default {@link Object#toString()} implementation for instantiated DataObjects. Each such generated
+     * interface contains this static method.
+     */
+    public static final @NonNull String BINDING_TO_STRING_NAME = "bindingToString";
+
     /**
      * Name of {@link ScalarTypeObject#getValue()}.
      */
index 2097215d779becebce2e053668d65dedb6177f8b..55df7d92372dc7c601f272f0c30fbe6372664f12 100644 (file)
@@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableSet.Builder;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.util.Arrays;
@@ -657,6 +658,11 @@ public final class BindingReflections {
             return false;
         }
         for (Method potentialMethod : potential.getMethods()) {
+            if (Modifier.isStatic(potentialMethod.getModifiers())) {
+                // Skip any static methods, as we are not interested in those
+                continue;
+            }
+
             try {
                 Method targetMethod = target.getMethod(potentialMethod.getName(), potentialMethod.getParameterTypes());
                 if (!potentialMethod.getReturnType().equals(targetMethod.getReturnType())) {