Generate bindingToString() and use it in generated implementations
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / java / api / generator / BuilderImplTemplate.xtend
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.binding.java.api.generator
9
10 import static org.opendaylight.mdsal.binding.model.util.BindingTypes.DATA_OBJECT
11 import static org.opendaylight.mdsal.binding.model.util.Types.STRING;
12 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD
13 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTABLE_AUGMENTATION_NAME
14 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.BINDING_TO_STRING_NAME
15 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME
16
17 import java.util.Collection
18 import java.util.List
19 import org.opendaylight.mdsal.binding.model.api.AnnotationType
20 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
21 import org.opendaylight.mdsal.binding.model.api.GeneratedType
22 import org.opendaylight.mdsal.binding.model.api.Type
23 import org.opendaylight.mdsal.binding.model.util.Types
24 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
25 import org.opendaylight.yangtools.yang.binding.AbstractAugmentable
26 import org.opendaylight.mdsal.binding.model.api.MethodSignature.ValueMechanics
27
28 class BuilderImplTemplate extends AbstractBuilderTemplate {
29     val Type builderType;
30
31     new(BuilderTemplate builder, GeneratedType type) {
32         super(builder.javaType.getEnclosedType(type.identifier), type, builder.targetType, builder.properties,
33             builder.augmentType, builder.keyType)
34         this.builderType = builder.type
35     }
36
37     override body() '''
38         «targetType.annotations.generateDeprecatedAnnotation»
39         private static final class «type.name»
40             «val impIface = targetType.importedName»
41             «IF augmentType !== null»
42                 extends «AbstractAugmentable.importedName»<«impIface»>
43             «ENDIF»
44             implements «impIface» {
45
46             «generateFields(true)»
47
48             «generateCopyConstructor(builderType, type)»
49
50             «generateGetters(true)»
51
52             «generateHashCode()»
53
54             «generateEquals()»
55
56             «generateToString()»
57         }
58     '''
59
60     override generateDeprecatedAnnotation(AnnotationType ann) {
61         return generateAnnotation(ann)
62     }
63
64     /**
65      * Template method which generates the method <code>hashCode()</code>.
66      *
67      * @return string with the <code>hashCode()</code> method definition in JAVA format
68      */
69     def protected generateHashCode() '''
70         «IF !properties.empty || augmentType !== null»
71             private int hash = 0;
72             private volatile boolean hashValid = false;
73
74             @«OVERRIDE.importedName»
75             public int hashCode() {
76                 if (hashValid) {
77                     return hash;
78                 }
79
80                 «hashCodeResult(properties)»
81                 «IF augmentType !== null»
82                     result = prime * result + «JU_OBJECTS.importedName».hashCode(augmentations());
83                 «ENDIF»
84
85                 hash = result;
86                 hashValid = true;
87                 return result;
88             }
89         «ENDIF»
90     '''
91
92     /**
93      * Template method which generates the method <code>equals()</code>.
94      *
95      * @return string with the <code>equals()</code> method definition in JAVA format
96      */
97     def protected generateEquals() '''
98         «IF !properties.empty || augmentType !== null»
99             @«OVERRIDE.importedName»
100             public boolean equals(«Types.objectType().importedName» obj) {
101                 if (this == obj) {
102                     return true;
103                 }
104                 if (!(obj instanceof «DATA_OBJECT.importedName»)) {
105                     return false;
106                 }
107                 if (!«targetType.importedName».class.equals(((«DATA_OBJECT.importedName»)obj).«DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME»())) {
108                     return false;
109                 }
110                 «targetType.importedName» other = («targetType.importedName»)obj;
111                 «FOR property : properties»
112                     «val fieldName = property.fieldName»
113                     if (!«property.importedUtilClass».equals(«fieldName», other.«property.getterName»())) {
114                         return false;
115                     }
116                 «ENDFOR»
117                 «IF augmentType !== null»
118                     if (getClass() == obj.getClass()) {
119                         // Simple case: we are comparing against self
120                         «type.name» otherImpl = («type.name») obj;
121                         if (!«JU_OBJECTS.importedName».equals(augmentations(), otherImpl.augmentations())) {
122                             return false;
123                         }
124                     } else {
125                         // Hard case: compare our augments with presence there...
126                         for («JU_MAP.importedName».Entry<«CLASS.importedName»<? extends «augmentType.importedName»>, «augmentType.importedName»> e : augmentations().entrySet()) {
127                             if (!e.getValue().equals(other.«AUGMENTABLE_AUGMENTATION_NAME»(e.getKey()))) {
128                                 return false;
129                             }
130                         }
131                         // .. and give the other one the chance to do the same
132                         if (!obj.equals(this)) {
133                             return false;
134                         }
135                     }
136                 «ENDIF»
137                 return true;
138             }
139         «ENDIF»
140     '''
141
142     /**
143      * Template method which generates the method <code>toString()</code>.
144      *
145      * @return string with the <code>toString()</code> method definition in JAVA format
146      */
147     def protected generateToString() '''
148         @«OVERRIDE.importedName»
149         public «STRING.importedName» toString() {
150             return «targetType.importedName».«BINDING_TO_STRING_NAME»(this);
151         }
152     '''
153
154     override protected generateCopyKeys(List<GeneratedProperty> keyProps) '''
155         if (base.«BindingMapping.IDENTIFIABLE_KEY_NAME»() != null) {
156             this.key = base.«BindingMapping.IDENTIFIABLE_KEY_NAME»();
157         } else {
158             this.key = new «keyType.importedName»(«FOR keyProp : keyProps SEPARATOR ", "»base.«keyProp.getterMethodName»()«ENDFOR»);
159         }
160         «FOR field : keyProps»
161             this.«field.fieldName» = key.«field.getterMethodName»();
162         «ENDFOR»
163     '''
164
165     override protected CharSequence generateCopyNonKeys(Collection<BuilderGeneratedProperty> props) '''
166         «FOR field : props»
167             «IF field.mechanics === ValueMechanics.NULLIFY_EMPTY»
168                 this.«field.fieldName» = «CODEHELPERS.importedName».emptyToNull(base.«field.getterName»());
169             «ELSE»
170                 this.«field.fieldName» = base.«field.getterName»();
171             «ENDIF»
172         «ENDFOR»
173     '''
174
175     override protected generateCopyAugmentation(Type implType) '''
176         super(base.«AUGMENTATION_FIELD»);
177     '''
178 }