BUG-1954: Fix useless Boolean instantiation
[mdsal.git] / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / yangtools / sal / java / api / generator / ClassTemplate.xtend
1 /*
2  * Copyright (c) 2014 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 package org.opendaylight.yangtools.sal.java.api.generator
9
10 import com.google.common.collect.ImmutableList
11 import com.google.common.collect.Lists
12 import com.google.common.collect.Range
13 import com.google.common.io.BaseEncoding
14 import java.beans.ConstructorProperties
15 import java.math.BigDecimal
16 import java.math.BigInteger
17 import java.util.ArrayList
18 import java.util.Arrays
19 import java.util.Collections
20 import java.util.List
21 import java.util.regex.Pattern
22 import org.opendaylight.yangtools.binding.generator.util.TypeConstants
23 import org.opendaylight.yangtools.sal.binding.model.api.Constant
24 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
25 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
26 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
27 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
28 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
29
30 /**
31  * Template for generating JAVA class.
32  */
33 class ClassTemplate extends BaseTemplate {
34
35     protected val List<GeneratedProperty> properties
36     protected val List<GeneratedProperty> finalProperties
37     protected val List<GeneratedProperty> parentProperties
38     protected val Iterable<GeneratedProperty> allProperties;
39     protected val Restrictions restrictions
40
41     /**
42      * List of enumeration which are generated as JAVA enum type.
43      */
44     protected val List<Enumeration> enums
45
46     /**
47      * List of constant instances which are generated as JAVA public static final attributes.
48      */
49     protected val List<Constant> consts
50
51     /**
52      * List of generated types which are enclosed inside <code>genType</code>
53      */
54     protected val List<GeneratedType> enclosedGeneratedTypes;
55     
56     protected val GeneratedTransferObject genTO;
57
58     /**
59      * Creates instance of this class with concrete <code>genType</code>.
60      * 
61      * @param genType generated transfer object which will be transformed to JAVA class source code
62      */
63     new(GeneratedTransferObject genType) {
64         super(genType)
65         this.genTO = genType
66         this.properties = genType.properties
67         this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
68         this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
69         this.restrictions = genType.restrictions
70
71         var List<GeneratedProperty> sorted = new ArrayList<GeneratedProperty>();
72         sorted.addAll(properties);
73         sorted.addAll(parentProperties);
74         Collections.sort(sorted, [p1, p2|
75             p1.name.compareTo(p2.name)
76         ]);
77
78         this.allProperties = sorted
79         this.enums = genType.enumerations
80         this.consts = genType.constantDefinitions
81         this.enclosedGeneratedTypes = genType.enclosedTypes
82     }
83
84     /**
85      * Generates JAVA class source code (class body only).
86      * 
87      * @return string with JAVA class body source code
88      */
89     def CharSequence generateAsInnerClass() {
90         return generateBody(true)
91     }
92
93     override protected body() {
94         generateBody(false);
95     }
96
97     /**
98      * Template method which generates class body.
99      * 
100      * @param isInnerClass boolean value which specify if generated class is|isn't inner
101      * @return string with class source code in JAVA format
102      */
103     def protected generateBody(boolean isInnerClass) '''
104         «wrapToDocumentation(formatDataForJavaDoc(type))»
105         «generateClassDeclaration(isInnerClass)» {
106             «suidDeclaration»
107             «innerClassesDeclarations»
108             «enumDeclarations»
109             «constantsDeclarations»
110             «generateFields»
111             
112             «IF restrictions != null && (!restrictions.rangeConstraints.nullOrEmpty || 
113                 !restrictions.lengthConstraints.nullOrEmpty)»
114             «generateConstraints»
115             
116             «ENDIF»
117             «constructors»
118
119             «defaultInstance»
120
121             «FOR field : properties SEPARATOR "\n"»
122                 «field.getterMethod»
123                 «IF !field.readOnly»
124                     «field.setterMethod»
125                 «ENDIF»
126             «ENDFOR»
127
128             «generateHashCode»
129
130             «generateEquals»
131
132             «generateToString(genTO.toStringIdentifiers)»
133
134             «generateLengthMethod("length", "_length")»
135
136             «generateRangeMethod("range", "_range")»
137
138         }
139     '''
140
141     def private generateLengthMethod(String methodName, String varName) '''
142         «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
143             «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
144             public static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> «methodName»() {
145                 return «varName»;
146             }
147         «ENDIF»
148     '''
149
150     def private generateRangeMethod(String methodName, String varName) '''
151         «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
152             «val returnType = allProperties.iterator.next.returnType»
153             public static «List.importedName»<«Range.importedName»<«returnType.importedNumber»>> «methodName»() {
154                 return «varName»;
155             }
156         «ENDIF»
157     '''
158
159     /**
160      * Template method which generates inner classes inside this interface.
161      * 
162      * @return string with the source code for inner classes in JAVA format
163      */
164     def protected innerClassesDeclarations() '''
165         «IF !enclosedGeneratedTypes.empty»
166             «FOR innerClass : enclosedGeneratedTypes SEPARATOR "\n"»
167                 «IF (innerClass instanceof GeneratedTransferObject)»
168                     «val classTemplate = new ClassTemplate(innerClass as GeneratedTransferObject)»
169                     «classTemplate.generateAsInnerClass»
170
171                 «ENDIF»
172             «ENDFOR»
173         «ENDIF»
174     '''
175
176     def protected constructors() '''
177         «IF genTO.unionType»
178             «genUnionConstructor»
179         «ELSE»
180             «allValuesConstructor»
181         «ENDIF»
182         «IF !allProperties.empty»
183             «copyConstructor»
184         «ENDIF»
185         «IF properties.empty && !parentProperties.empty »
186             «parentConstructor»
187         «ENDIF»
188     '''
189
190     def private generateConstraints() '''
191         static {
192             «IF !restrictions.rangeConstraints.nullOrEmpty»
193             «generateRangeConstraints»
194             «ENDIF»
195             «IF !restrictions.lengthConstraints.nullOrEmpty»
196             «generateLengthConstraints»
197             «ENDIF»
198         }
199     '''
200
201     private def generateRangeConstraints() '''
202         «IF !allProperties.nullOrEmpty»
203             «val returnType = allProperties.iterator.next.returnType»
204             «IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
205                 «rangeBody(restrictions, BigDecimal, genTO.importedName, "_range")»
206             «ELSE»
207                 «rangeBody(restrictions, BigInteger, genTO.importedName, "_range")»
208             «ENDIF»
209         «ENDIF»
210     '''
211
212     private def rangeBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
213         «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
214         «FOR r : restrictions.rangeConstraints»
215             builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
216         «ENDFOR»
217         «varName» = builder.build();
218     '''
219
220     private def lengthBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
221         «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
222         «FOR r : restrictions.lengthConstraints»
223             builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
224         «ENDFOR»
225         «varName» = builder.build();
226     '''
227
228     private def generateLengthConstraints() '''
229         «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
230             «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
231             «IF numberClass.equals(typeof(BigDecimal))»
232                 «lengthBody(restrictions, numberClass, genTO.importedName, "_length")»
233             «ELSE»
234                 «lengthBody(restrictions, typeof(BigInteger), genTO.importedName, "_length")»
235             «ENDIF»
236         «ENDIF»
237     '''
238
239     def protected allValuesConstructor() '''
240     «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
241         @«ConstructorProperties.importedName»("value")
242     «ENDIF»
243     public «type.name»(«allProperties.asArgumentsDeclaration») {
244         «IF false == parentProperties.empty»
245             super(«parentProperties.asArguments»);
246         «ENDIF»
247         «FOR p : allProperties» 
248             «generateRestrictions(type, p.fieldName.toString, p.returnType)»
249         «ENDFOR»
250         «FOR p : properties» 
251             this.«p.fieldName» = «p.fieldName»;
252         «ENDFOR»
253     }
254     
255     '''
256
257     def protected genUnionConstructor() '''
258     «FOR p : allProperties»
259         «val List<GeneratedProperty> other = new ArrayList(properties)»
260         «IF other.remove(p)»
261             «genConstructor(p, other)»
262         «ENDIF»
263     «ENDFOR»
264
265     '''
266
267     def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
268     public «type.name»(«property.returnType.importedName + " " + property.name») {
269         «IF false == parentProperties.empty»
270             super(«parentProperties.asArguments»);
271         «ENDIF»
272             «generateRestrictions(type, property.fieldName.toString, property.returnType)»
273             this.«property.fieldName» = «property.name»;
274             «FOR p : other»
275             this.«p.fieldName» = null;
276             «ENDFOR»
277     }
278     '''
279
280     def protected copyConstructor() '''
281     /**
282      * Creates a copy from Source Object.
283      *
284      * @param source Source object
285      */
286     public «type.name»(«type.name» source) {
287         «IF false == parentProperties.empty»
288             super(source);
289         «ENDIF»
290         «FOR p : properties» 
291             this.«p.fieldName» = source.«p.fieldName»;
292         «ENDFOR»
293     }
294     '''
295
296     def protected parentConstructor() '''
297     /**
298      * Creates a new instance from «genTO.superType.importedName»
299      *
300      * @param source Source object
301      */
302     public «type.name»(«genTO.superType.importedName» source) {
303             super(source);
304     }
305     '''
306
307     def protected defaultInstance() '''
308         «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
309             «val prop = allProperties.get(0)»
310             «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
311             public static «genTO.name» getDefaultInstance(String defaultValue) {
312                 «IF "byte[]".equals(prop.returnType.name)»
313                     «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64(); 
314                     return new «genTO.name»(baseEncoding.decode(defaultValue));
315                 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
316                     return new «genTO.name»(defaultValue);
317                 «ELSEIF allProperties.size > 1»
318                     «bitsArgs»
319                 «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
320                     return new «genTO.name»(Boolean.valueOf(defaultValue));
321                 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
322                     return new «genTO.name»(Byte.valueOf(defaultValue));
323                 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
324                     return new «genTO.name»(Short.valueOf(defaultValue));
325                 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
326                     return new «genTO.name»(Integer.valueOf(defaultValue));
327                 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
328                     return new «genTO.name»(Long.valueOf(defaultValue));
329                 «ELSE»
330                     return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
331                 «ENDIF»
332             }
333             «ENDIF»
334         «ENDIF»
335     '''
336
337     def protected bitsArgs() '''
338         «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
339         if (!properties.contains(defaultValue)) {
340             throw new «IllegalArgumentException.importedName»("invalid default parameter");
341         }
342         int i = 0;
343         return new «genTO.name»(
344         «FOR prop : allProperties SEPARATOR ","»
345             properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
346         «ENDFOR»
347         );
348     '''
349
350     def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
351         «FOR prop : properties SEPARATOR ","»
352             "«prop.name»"
353         «ENDFOR»
354     '''
355
356     /**
357      * Template method which generates JAVA class declaration.
358      * 
359      * @param isInnerClass boolean value which specify if generated class is|isn't inner
360      * @return string with class declaration in JAVA format
361      */
362     def protected generateClassDeclaration(boolean isInnerClass) '''
363         public«
364         IF (isInnerClass)»«
365             " static final "»«
366         ELSEIF (type.abstract)»«
367             " abstract "»«
368         ELSE»«
369             " "»«
370         ENDIF»class «type.name»«
371         IF (genTO.superType != null)»«
372             " extends "»«genTO.superType.importedName»«
373         ENDIF»
374         «IF (!type.implements.empty)»«
375             " implements "»«
376             FOR type : type.implements SEPARATOR ", "»«
377                 type.importedName»«
378             ENDFOR»«
379         ENDIF
380     »'''
381
382     /**
383      * Template method which generates JAVA enum type.
384      * 
385      * @return string with inner enum source code in JAVA format
386      */
387     def protected enumDeclarations() '''
388         «IF !enums.empty»
389             «FOR e : enums SEPARATOR "\n"»
390                 «val enumTemplate = new EnumTemplate(e)»
391                 «enumTemplate.generateAsInnerClass»
392             «ENDFOR»
393         «ENDIF»
394     '''
395
396     def protected suidDeclaration() '''
397         «IF genTO.SUID != null»
398             private static final long serialVersionUID = «genTO.SUID.value»L; 
399         «ENDIF»
400     '''
401
402     /**
403      * Template method wich generates JAVA constants.
404      * 
405      * @return string with constants in JAVA format 
406      */
407     def protected constantsDeclarations() '''
408         «IF !consts.empty»
409             «FOR c : consts»
410                 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
411                     «val cValue = c.value»
412                     «IF cValue instanceof List<?>»
413                         «val cValues = cValue as List<?>»
414                         private static final «List.importedName»<«Pattern.importedName»> «Constants.MEMBER_PATTERN_LIST» = new «ArrayList.importedName»<«Pattern.importedName»>();
415                         public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «Arrays.importedName».asList(«
416                         FOR v : cValues SEPARATOR ", "»«
417                             IF v instanceof String»"«
418                                 v as String»"«
419                             ENDIF»«
420                         ENDFOR»);
421
422                         «generateStaticInicializationBlock»
423                     «ENDIF»
424                 «ELSE»
425                     public static final «c.type.importedName» «c.name» = «c.value»;
426                 «ENDIF»
427             «ENDFOR»
428         «ENDIF»
429     '''
430
431     /**
432      * Template method which generates JAVA static initialization block.
433      *
434      * @return string with static initialization block in JAVA format
435      */
436     def protected generateStaticInicializationBlock() '''
437         static {
438             for (String regEx : «TypeConstants.PATTERN_CONSTANT_NAME») {
439                 «Constants.MEMBER_PATTERN_LIST».add(Pattern.compile(regEx));
440             }
441         }
442     '''
443
444     /**
445      * Template method which generates JAVA class attributes.
446      *
447      * @return string with the class attributes in JAVA format
448      */
449     def protected generateFields() '''
450         «IF restrictions != null»
451             «val prop = getPropByName("value")»
452             «IF prop != null»
453                 «IF !(restrictions.lengthConstraints.empty)»
454                     private static final «List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _length;
455                 «ENDIF»
456                 «IF !(restrictions.rangeConstraints.empty)»
457                     private static final «List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _range;
458                 «ENDIF»
459             «ENDIF»
460         «ENDIF»
461         «IF !properties.empty»
462             «FOR f : properties»
463                 «IF f.readOnly»final«ENDIF» private «f.returnType.importedName» «f.fieldName»;
464             «ENDFOR»
465         «ENDIF»
466     '''
467
468     /**
469      * Template method which generates the method <code>hashCode()</code>.
470      *
471      * @return string with the <code>hashCode()</code> method definition in JAVA format
472      */
473     def protected generateHashCode() '''
474         «IF !genTO.hashCodeIdentifiers.empty»
475             @Override
476             public int hashCode() {
477                 final int prime = 31;
478                 int result = 1;
479                 «FOR property : genTO.hashCodeIdentifiers»
480                     «IF property.returnType.name.contains("[")»
481                     result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
482                     «ELSE»
483                     result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
484                     «ENDIF»
485                 «ENDFOR»
486                 return result;
487             }
488         «ENDIF»
489     '''
490
491     /**
492      * Template method which generates the method <code>equals()</code>.
493      *
494      * @return string with the <code>equals()</code> method definition in JAVA format
495      */
496     def protected generateEquals() '''
497         «IF !genTO.equalsIdentifiers.empty»
498             @Override
499             public boolean equals(java.lang.Object obj) {
500                 if (this == obj) {
501                     return true;
502                 }
503                 if (obj == null) {
504                     return false;
505                 }
506                 if (getClass() != obj.getClass()) {
507                     return false;
508                 }
509                 «type.name» other = («type.name») obj;
510                 «FOR property : genTO.equalsIdentifiers»
511                     «val fieldName = property.fieldName»
512                     if («fieldName» == null) {
513                         if (other.«fieldName» != null) {
514                             return false;
515                         }
516                     «IF property.returnType.name.contains("[")»
517                     } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
518                     «ELSE»
519                     } else if(!«fieldName».equals(other.«fieldName»)) {
520                     «ENDIF»
521                         return false;
522                     }
523                 «ENDFOR»
524                 return true;
525             }
526         «ENDIF»
527     '''
528
529     def GeneratedProperty getPropByName(String name) {
530         for (GeneratedProperty prop : allProperties) {
531             if (prop.name.equals(name)) {
532                 return prop;
533             }
534         }
535         return null;
536     }
537
538 }