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