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