Code generator prototype - Binding specification v2
[mdsal.git] / binding2 / mdsal-binding2-java-api-generator / src / main / twirl / org / opendaylight / mdsal / binding / javav2 / java / api / generator / builderTemplate.scala.txt
1 @*
2  * Copyright (c) 2016 Cisco Systems, Inc. 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
9 @import java.util.List
10 @import org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.fieldName
11 @import org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.formatDataForJavaDocBuilder
12 @import org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.getSimpleNameForBuilder
13 @import org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.getterMethodName
14 @import org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.getRestrictions
15 @import org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.propertyNameFromGetter
16 @import org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.toFirstUpper
17 @import org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.wrapToDocumentation
18 @import org.opendaylight.mdsal.binding.javav2.java.api.generator.renderers.BuilderRenderer.getAllIfcs
19 @import org.opendaylight.mdsal.binding.javav2.java.api.generator.renderers.BuilderRenderer.hasImplementsFromUses
20 @import org.opendaylight.mdsal.binding.javav2.java.api.generator.renderers.BuilderRenderer.toListOfNames
21 @import org.opendaylight.mdsal.binding.javav2.java.api.generator.rangeGenerators.AbstractRangeGenerator
22 @import org.opendaylight.mdsal.binding.javav2.java.api.generator.rangeGenerators.LengthGenerator
23 @import org.opendaylight.mdsal.binding.javav2.model.api.ConcreteType
24 @import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType
25 @import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject
26 @import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedProperty
27 @import org.opendaylight.mdsal.binding.javav2.model.api.Type
28 @import org.opendaylight.yangtools.concepts.Builder
29
30 @(genType: GeneratedType, properties: Set[GeneratedProperty], importedNames: Map[String, String],
31 ImportedNamesWithProperties: Map[GeneratedProperty, String], augmentField: GeneratedProperty, copyConstructorHelper: String,
32 getterMethods: List[String])
33 @if(genType != null) {
34 @{wrapToDocumentation(formatDataForJavaDocBuilder(importedNames.get("genType")))}
35 public class @{genType.getName}Builder implements @{getSimpleNameForBuilder} <@{importedNames.get("genType")}> {
36
37     @generateFields(false)
38
39     @generateAugmentField(false)
40
41     @generateConstructorsFromIfcs()
42
43     @generateCopyConstructor(false)
44
45     @generateMethodFieldsFrom()
46
47     @generateGetters(false)
48
49     @generateSetters()
50
51     @@Override
52     public @{genType.getName} build() {
53         return new @{genType.getName}Impl(this);
54     }
55
56     private static final class @{genType.getName}Impl implements @{genType.getName} {
57
58         @implementedInterfaceGetter()
59
60         @generateFields(true)
61
62         @generateAugmentField(true)
63
64         @generateCopyConstructor(true)
65
66         @generateGetters(true)
67
68         @generateHashCode()
69
70         @generateEquals()
71
72         @generateToString()
73     }
74 }
75 }
76
77 @**
78  * Template method which generates class attributes.
79  *
80  * @param isFinal value which specify whether field is|isn't final
81  * @param genType is genType
82  * @return string with class attributes and their types
83  *@
84 @generateFields(isFinal: Boolean) = {
85     @if(ImportedNamesWithProperties != null) {
86         @for((key, value) <- ImportedNamesWithProperties) {
87             private @if(isFinal) { final}
88             @{value} @{fieldName(key)};
89         }
90     }
91 }
92
93 @**
94  * Template method which generates class attributes.
95  *
96  * @param boolean value which specify whether field is|isn't final
97  * @return string with class attributes and their types
98  *@
99 @generateAugmentField(isPrivate: Boolean) = {
100     @if(augmentField != null) {
101         @if(isPrivate) {private }
102         @{importedNames.get("map")}<@{importedNames.get("class")}<? extends @{importedNames.get("augmentFieldReturnType")}>,
103         @{importedNames.get("augmentFieldReturnType")}> @{augmentField.getName} = @{importedNames.get("collections")}.emptyMap();
104     }
105 }
106
107 @implementedInterfaceGetter() = {
108     public @{importedNames.get("class")}<@{importedNames.get("genType")}> getImplementedInterface() {
109     return @{importedNames.get("genType")}.class;
110     }
111 }
112
113 @**
114  * Generate default constructor and constructor for every implemented interface from uses statements.
115  *@
116 @generateConstructorsFromIfcs() = {
117     public @{genType.getName}Builder() {
118     }
119     @if(genType.isInstanceOf[GeneratedType] && !genType.isInstanceOf[GeneratedTransferObject]) {
120         @for(impl <- genType.asInstanceOf[GeneratedType].getImplements) {
121             @generateConstructorFromIfc(impl)
122         }
123     }
124 }
125
126 @generateMethodFieldsFrom() = {
127     @if(genType.isInstanceOf[GeneratedType] && !genType.isInstanceOf[GeneratedTransferObject]) {
128         @if(hasImplementsFromUses(genType.asInstanceOf[GeneratedType])) {
129             /**
130              *Set fields from given grouping argument. Valid argument is instance of one of following types:
131              * <ul>
132              @for(impl <- getAllIfcs(genType.asInstanceOf[GeneratedType])) {
133              * <li>@{impl.getFullyQualifiedName}</li>
134              }
135              * </ul>
136              *
137              * @@param arg grouping object
138              * @@throws IllegalArgumentException if given argument is none of valid types
139             */
140
141             public void fieldsFrom(@{importedNames.get("treeNode")} arg) {
142                 boolean isValidArg = false;
143                 @for(impl <- getAllIfcs(genType.asInstanceOf[GeneratedType])) {
144                     @if(impl.isInstanceOf[GeneratedType] && !impl.asInstanceOf[GeneratedType].getMethodDefinitions.isEmpty) {
145                         if (arg instanceof @{impl.asInstanceOf[GeneratedType].getFullyQualifiedName}) {
146                             @if(!impl.isInstanceOf[GeneratedTransferObject]) {
147                                 @for(getter <- genType.asInstanceOf[GeneratedType].getMethodDefinitions) {
148                                     this._@{propertyNameFromGetter(getter)} = ((@{impl.asInstanceOf[GeneratedType].getFullyQualifiedName})arg).@{getter.getName}();
149                                 }
150                             }
151                             isValidArg = true;
152                         }
153                     }
154                 }
155                 if (!isValidArg) {
156                     throw new IllegalArgumentException(
157                       "expected one of: @{toListOfNames(getAllIfcs(genType.asInstanceOf[GeneratedType]))} \n" +
158                       "but was: " + arg
159                     );
160                 }
161             }
162         }
163     }
164 }
165
166 @**
167  * Generate constructor with argument of given type.
168  *@
169 @generateConstructorFromIfc(impl: Type) = {
170     @if(impl.isInstanceOf[GeneratedType]) {
171         @if(!impl.asInstanceOf[GeneratedType].getMethodDefinitions.isEmpty) {
172             public @{genType.getName}Builder(
173             @{impl.getFullyQualifiedName} arg) {
174             @{printConstructorPropertySetter(impl)}
175             }
176         }
177         @for(implTypeImplement <- impl.asInstanceOf[GeneratedType].getImplements) {
178             @generateConstructorFromIfc(implTypeImplement)
179         }
180     }
181 }
182
183 @printConstructorPropertySetter(implementedIfc: Type) = {
184     @if(implementedIfc.isInstanceOf[GeneratedType] && !implementedIfc.isInstanceOf[GeneratedTransferObject]) {
185         @for(getter <- implementedIfc.asInstanceOf[GeneratedType].getMethodDefinitions) {
186             this._@{propertyNameFromGetter(getter)} = arg.@{getter.getName}();
187         }
188         @for(impl <- implementedIfc.asInstanceOf[GeneratedType].getImplements) {
189             @{printConstructorPropertySetter(impl)}
190         }
191     }
192 }
193
194 @generateCopyConstructor(impl: Boolean) = {
195     @if(impl) {private} else {public}
196     @{genType.getName}
197     @if(impl) {Impl} else {Builder}
198     (@{genType.getName}
199     @if(impl) {Builder} base) {
200     @{copyConstructorHelper}
201     @if(augmentField != null) {
202         @if(impl) {
203             switch (base.@{augmentField.getName}.size()) {
204             case 0:
205                 this.@{augmentField.getName} = @{importedNames.get("collections")}.emptyMap();
206                 break;
207             case 1:
208                 final @{importedNames.get("map")}.Entry<@{importedNames.get("class")}<? extends @{importedNames.get("augmentFieldReturnType")}>, @{importedNames.get("augmentFieldReturnType")}> e = base.@{augmentField.getName}.entrySet().iterator().next();
209                 this.@{augmentField.getName} = @{importedNames.get("collections")}.<@{importedNames.get("class")}<? extends @{importedNames.get("augmentFieldReturnType")}>, @{importedNames.get("augmentFieldReturnType")}> singletonMap(e.getKey(), e.getValue());
210                 break;
211             default :
212                 this.@{augmentField.getName} = new @{importedNames.get("hashMap")}<>(base.@{augmentField.getName});
213             }
214         } else {
215             if (base instanceof @{genType.getName}Impl) {
216                 @{genType.getName}Impl impl = (@{genType.getName}Impl) base;
217                 if (!impl.@{augmentField.getName}.isEmpty()) {
218                     this.@{augmentField.getName} = new @{importedNames.get("hashMap")}<>(impl.@{augmentField.getName});
219                 }
220             } @{"else"} if (base instanceof @{importedNames.get("augmentationHolder")}) {
221                 @@SuppressWarnings("unchecked")
222                 @{importedNames.get("augmentationHolder")}<@{importedNames.get("genType")}> casted =(@{importedNames.get("augmentationHolder")}<@{importedNames.get("genType")}>) base;
223                 if (!casted.augmentations().isEmpty()) {
224                     this.@{augmentField.getName} = new @{importedNames.get("hashMap")}<>(casted.augmentations());
225                 }
226             }
227         }
228     }
229     }
230 }
231
232
233 @generateSetters() = {
234     @for(field <- properties) {
235         @if(!field.getReturnType.isInstanceOf[GeneratedType] && getRestrictions(field.getReturnType) != null) {
236             @if(getRestrictions(field.getReturnType).getRangeConstraints != null && !getRestrictions(field.getReturnType).getRangeConstraints.isEmpty) {
237                 @{AbstractRangeGenerator.forType(field.getReturnType).generateRangeChecker(toFirstUpper(field.getName),
238                 getRestrictions(field.getReturnType).getRangeConstraints)}
239             }
240             @if(getRestrictions(field.getReturnType).getLengthConstraints != null && !getRestrictions(field.getReturnType).getLengthConstraints.isEmpty) {
241                 @{LengthGenerator.generateLengthChecker(fieldName(field), field.getReturnType,
242                 getRestrictions(field.getReturnType).getLengthConstraints)}
243             }
244         }
245         public @{genType.getName}Builder set@{toFirstUpper(field.getName)}(final @{importedNames.get("augmentFieldReturnType")} value) {
246         @if(!field.getReturnType.isInstanceOf[GeneratedType] && getRestrictions(field.getReturnType) != null) {
247             if (value != null) {
248             @if(getRestrictions(field.getReturnType).getRangeConstraints != null && !getRestrictions(field.getReturnType).getRangeConstraints.isEmpty) {
249                 @if(field.getReturnType.isInstanceOf[ConcreteType]) {
250                     @{AbstractRangeGenerator.forType(field.getReturnType).generateRangeCheckerCall(toFirstUpper(field.getName), "value")}
251                 } else {
252                     @{AbstractRangeGenerator.forType(field.getReturnType).generateRangeCheckerCall(toFirstUpper(field.getName), "value.getValue()")}
253                 }
254             }
255             @if(getRestrictions(field.getReturnType).getLengthConstraints != null && !getRestrictions(field.getReturnType).getLengthConstraints.isEmpty) {
256                 @if(field.getReturnType.isInstanceOf[ConcreteType]) {
257                     @{LengthGenerator.generateLengthCheckerCall(fieldName(field), "value")}
258                 } else {
259                     @{LengthGenerator.generateLengthCheckerCall(fieldName(field), "value.getValue()")}
260                 }
261             }
262             }
263         }
264             this.@{fieldName(field)} = value;
265             return this;
266         }
267     }
268     @if(augmentField != null) {
269         public @{genType.getName}Builder add@{toFirstUpper(augmentField.getName)}(@{importedNames.get("class")}<? extends @{importedNames.get("augmentFieldReturnType")}> augmentationType, @{importedNames.get("augmentFieldReturnType")} augmentation) {
270             if (augmentation == null) {
271                 return remove@{toFirstUpper(augmentField.getName)}(augmentationType);
272             }
273
274             if (!(this.@{augmentField.getName} instanceof @{importedNames.get("hashMap")})) {
275                 this.@{augmentField.getName} = new @{importedNames.get("hashMap")}<>();
276             }
277
278             this.@{augmentField.getName}.put(augmentationType, augmentation);
279             return this;
280         }
281
282         public @{genType.getName}Builder remove@{toFirstUpper(augmentField.getName)}
283         (@{importedNames.get("class")}<? extends @{importedNames.get("augmentFieldReturnType")}> augmentationType) {
284             if (this.@{augmentField.getName} instanceof @{importedNames.get("hashMap")}) {
285                 this.@{augmentField.getName}.remove(augmentationType);
286             }
287             return this;
288         }
289     }
290 }
291
292 @generateGetters(addOverride: Boolean) = {
293     @if(!getterMethods.isEmpty) {
294         @for(property <- getterMethods) {
295             @if(addOverride) {@@Override}
296             @{property}
297         }
298     }
299     @if(augmentField != null) {
300         @@SuppressWarnings("unchecked")
301         @if(addOverride) {@@Override}
302         public <E extends @{importedNames.get("augmentFieldReturnType")}> E get@{toFirstUpper(augmentField.getName)}
303         (@{importedNames.get("class")}<E> augmentationType) {
304             if (augmentationType == null) {
305                 throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");
306             }
307             return (E) @{augmentField.getName}.get(augmentationType);
308         }
309     }
310 }
311
312 @generateHashCode() = {
313     @if(!properties.isEmpty || augmentField != null) {
314         private int hash = 0;
315         private volatile boolean hashValid = false;
316
317         @@Override
318         public int hashCode() {
319             if (hashValid) {
320                 return hash;
321             }
322
323             final int prime = 31;
324             int result = 1;
325             @for(property <- properties) {
326                 @if(property.getReturnType.getName.contains("[")) {
327                     result = prime * result + @{importedNames.get("arrays")}.hashCode(@{fieldName(property)});
328                 } else {
329                     result = prime * result + @{importedNames.get("objects")}.hashCode(@{fieldName(property)});
330                 }
331             }
332             @if(augmentField != null) {
333                 result = prime * result + @{importedNames.get("objects")}.hashCode(@{augmentField.getName});
334             }
335
336             hash = result;
337             hashValid = true;
338             return result;
339         }
340     }
341 }
342
343 @generateToString() = {
344     @if(properties != null) {
345         @@Override
346         public @{importedNames.get("string")} toString() {
347             @{importedNames.get("string")} name = "@{genType.getName} [";
348             @{importedNames.get("stringBuilder")} builder = new @{importedNames.get("stringBuilder")}(name);
349             @for((property, index) <- properties.zipWithIndex) {
350                 if (@{fieldName(property)} != null) {
351                     builder.append("@{fieldName(property)}=");
352                     @if(property.getReturnType.getName.contains("[")) {
353                         builder.append(@{importedNames.get("arrays")}.toString(@{fieldName(property)}));
354                     } else {
355                         builder.append(@{fieldName(property)});
356                     }
357                     @if(properties.size() > 1 && index < properties.size()-1){
358                         builder.append(", ");
359                     }
360                 }
361             }
362             @if(augmentField != null) {
363                 @if(!properties.isEmpty()){
364                     final int builderLength = builder.length();
365                     final int builderAdditionalLength = builder.substring(name.length(), builderLength).length();
366                     if (builderAdditionalLength > 2 && !builder.substring(builderLength - 2, builderLength).equals(", ")) {
367                         builder.append(", ");
368                     }
369                 }
370                 builder.append("@{augmentField.getName}=");
371                 builder.append(@{augmentField.getName}.values());
372                 return builder.append(']').toString();
373             } else {
374                 @if(properties.isEmpty()){
375                     return builder.append(']').toString();
376                 } else {
377                     return builder.append(']').toString();
378                 }
379             }
380         }
381     }
382 }
383
384 @generateEquals() = {
385     @if(!properties.isEmpty || augmentField != null) {
386         @@Override
387         public boolean equals(@{importedNames.get("object")} obj) {
388             if (this == obj) {
389                 return true;
390             }
391             if (!(obj instanceof @{importedNames.get("treeNode")})) {
392                 return false;
393             }
394             if (!@{importedNames.get("genType")}.class.equals(((@{importedNames.get("treeNode")})obj).getImplementedInterface)) {
395                 return false;
396             }
397             @{importedNames.get("genType")} other = (@{importedNames.get("genType")})obj;
398             @for(property <- properties) {
399                 @if(property.getReturnType.getName.contains("[")) {
400                     if (!@{importedNames.get("arrays")}.equals(@{fieldName(property)}, other.@{getterMethodName(property)}()))
401                 } else {
402                     if (!@{importedNames.get("objects")}.equals(@{fieldName(property)}, other.@{getterMethodName(property)}()))
403                 }
404                 {
405                     return false;
406                 }
407             }
408             @if(augmentField != null) {
409                 if (getClass() == obj.getClass()) {
410                     // Simple case: we are comparing against self
411                     @{genType.getName}Impl otherImpl = (@{genType.getName}Impl) obj;
412                     if (!@{importedNames.get("objects")}.equals(@{augmentField.getName}, otherImpl.@{augmentField.getName})) {
413                         return false;
414                     }
415                 } @{"else"} {
416                     // Hard case: compare our augments with presence there...
417                     for (@{importedNames.get("map")}.Entry<@{importedNames.get("class")}<? extends @{importedNames.get("augmentFieldReturnType")}>, @{importedNames.get("augmentFieldReturnType")}> e : @{augmentField.getName}.entrySet()) {
418                         if (!e.getValue().equals(other.getAugmentation(e.getKey()))) {
419                             return false;
420                         }
421                     }
422                     // .. and give the other one the chance to do the same
423                     if (!obj.equals(this)) {
424                         return false;
425                     }
426                 }
427             }
428             return true;
429         }
430     }
431 }