Reference java.lang.Override through importedName
[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     private 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             «FOR c : consts»
227                 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
228                 «CodeHelpers.importedName».checkPattern(_value, «Constants.MEMBER_PATTERN_LIST», «Constants.MEMBER_REGEX_LIST»);
229                 «ENDIF»
230             «ENDFOR»
231         «ENDIF»
232
233         «FOR p : properties»
234             «IF p.returnType.importedName.contains("[]")»
235                 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name
236                 .equals("value")»
237                 this.«p.fieldName» = «p.fieldName».clone();
238                 «ELSE»
239                 this.«p.fieldName» = «p.fieldName» == null ? null : «p.fieldName».clone();
240                 «ENDIF»
241             «ELSE»
242             this.«p.fieldName» = «p.fieldName»;
243             «ENDIF»
244         «ENDFOR»
245     }
246
247     '''
248
249     def protected genUnionConstructor() '''
250     «FOR p : allProperties»
251         «val List<GeneratedProperty> other = new ArrayList(properties)»
252         «IF other.remove(p)»
253             «genConstructor(p, other)»
254         «ENDIF»
255     «ENDFOR»
256
257     '''
258
259     def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
260     public «type.name»(«property.returnType.importedName + " " + property.name») {
261         «IF false == parentProperties.empty»
262             super(«parentProperties.asArguments»);
263         «ENDIF»
264
265         «generateRestrictions(type, property.fieldName.toString, property.returnType)»
266
267         this.«property.fieldName» = «property.name»;
268         «FOR p : other»
269             this.«p.fieldName» = null;
270         «ENDFOR»
271     }
272     '''
273
274     def private static paramValue(Type returnType, String paramName) {
275         if (returnType instanceof ConcreteType) {
276             return paramName
277         } else {
278             return paramName + ".getValue()"
279         }
280     }
281
282     def private generateRestrictions(Type type, String paramName, Type returnType) '''
283         «val restrictions = type.restrictions»
284         «IF restrictions !== null»
285             «IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
286             if («paramName» != null) {
287                 «IF restrictions.lengthConstraint.present»
288                     «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
289                 «ENDIF»
290                 «IF restrictions.rangeConstraint.present»
291                     «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
292                 «ENDIF»
293             }
294             «ENDIF»
295         «ENDIF»
296     '''
297
298     def protected copyConstructor() '''
299     /**
300      * Creates a copy from Source Object.
301      *
302      * @param source Source object
303      */
304     public «type.name»(«type.name» source) {
305         «IF false == parentProperties.empty»
306             super(source);
307         «ENDIF»
308         «FOR p : properties»
309             this.«p.fieldName» = source.«p.fieldName»;
310         «ENDFOR»
311     }
312     '''
313
314     def protected parentConstructor() '''
315     /**
316      * Creates a new instance from «genTO.superType.importedName»
317      *
318      * @param source Source object
319      */
320     public «type.name»(«genTO.superType.importedName» source) {
321             super(source);
322     }
323     '''
324
325     def protected defaultInstance() '''
326         «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
327             «val prop = allProperties.get(0)»
328             «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
329             public static «genTO.name» getDefaultInstance(String defaultValue) {
330                 «IF "byte[]".equals(prop.returnType.name)»
331                     «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64();
332                     return new «genTO.name»(baseEncoding.decode(defaultValue));
333                 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
334                     return new «genTO.name»(defaultValue);
335                 «ELSEIF allProperties.size > 1»
336                     «bitsArgs»
337                 «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
338                     return new «genTO.name»(«Boolean.importedName».valueOf(defaultValue));
339                 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
340                     return new «genTO.name»(«Byte.importedName».valueOf(defaultValue));
341                 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
342                     return new «genTO.name»(«Short.importedName».valueOf(defaultValue));
343                 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
344                     return new «genTO.name»(«Integer.importedName».valueOf(defaultValue));
345                 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
346                     return new «genTO.name»(«Long.importedName».valueOf(defaultValue));
347                 «ELSE»
348                     return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
349                 «ENDIF»
350             }
351             «ENDIF»
352         «ENDIF»
353     '''
354
355     def protected bitsArgs() '''
356         «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
357         if (!properties.contains(defaultValue)) {
358             throw new «IllegalArgumentException.importedName»("invalid default parameter");
359         }
360         int i = 0;
361         return new «genTO.name»(
362         «FOR prop : allProperties SEPARATOR ","»
363             properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
364         «ENDFOR»
365         );
366     '''
367
368     def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
369         «FOR prop : properties SEPARATOR ","»
370             "«prop.name»"
371         «ENDFOR»
372     '''
373
374     /**
375      * Template method which generates JAVA class declaration.
376      *
377      * @param isInnerClass boolean value which specify if generated class is|isn't inner
378      * @return string with class declaration in JAVA format
379      */
380     def protected generateClassDeclaration(boolean isInnerClass) '''
381         public«
382         IF (isInnerClass)»«
383             " static final "»«
384         ELSEIF (type.abstract)»«
385             " abstract "»«
386         ELSE»«
387             " "»«
388         ENDIF»class «type.name»«
389         IF (genTO.superType !== null)»«
390             " extends "»«genTO.superType.importedName»«
391         ENDIF»
392         «IF (!type.implements.empty)»«
393             " implements "»«
394             FOR type : type.implements SEPARATOR ", "»«
395                 type.importedName»«
396             ENDFOR»«
397         ENDIF
398     »'''
399
400     /**
401      * Template method which generates JAVA enum type.
402      *
403      * @return string with inner enum source code in JAVA format
404      */
405     def protected enumDeclarations() '''
406         «IF !enums.empty»
407             «FOR e : enums SEPARATOR "\n"»
408                 «new EnumTemplate(javaType.getEnclosedType(e.identifier), e).generateAsInnerClass»
409             «ENDFOR»
410         «ENDIF»
411     '''
412
413     def protected suidDeclaration() '''
414         «IF genTO.SUID !== null»
415             private static final long serialVersionUID = «genTO.SUID.value»L;
416         «ENDIF»
417     '''
418
419     def protected annotationDeclaration() '''
420         «IF genTO.getAnnotations !== null»
421             «FOR e : genTO.getAnnotations»
422                 @«e.getName»
423             «ENDFOR»
424         «ENDIF»
425     '''
426
427     /**
428      * Template method which generates JAVA constants.
429      *
430      * @return string with constants in JAVA format
431      */
432     def protected constantsDeclarations() '''
433         «IF !consts.empty»
434             «FOR c : consts»
435                 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
436                     «val cValue = c.value as Map<String, String>»
437                     public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
438                     FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
439                     «IF cValue.size == 1»
440                         private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST» = «Pattern.importedName».compile(«TypeConstants.PATTERN_CONSTANT_NAME».get(0));
441                         private static final String «Constants.MEMBER_REGEX_LIST» = "«cValue.values.get(0).escapeJava»";
442                     «ELSE»
443                         private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST» = «CodeHelpers.importedName».compilePatterns(«TypeConstants.PATTERN_CONSTANT_NAME»);
444                         private static final String[] «Constants.MEMBER_REGEX_LIST» = { «
445                         FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
446                     «ENDIF»
447                 «ELSE»
448                     «emitConstant(c)»
449                 «ENDIF»
450             «ENDFOR»
451         «ENDIF»
452     '''
453
454     /**
455      * Template method which generates JAVA class attributes.
456      *
457      * @return string with the class attributes in JAVA format
458      */
459     def protected generateFields() '''
460         «IF !properties.empty»
461             «FOR f : properties»
462                 private«IF isReadOnly(f)» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
463             «ENDFOR»
464         «ENDIF»
465     '''
466
467     protected def isReadOnly(GeneratedProperty field) {
468         return field.readOnly
469     }
470
471     /**
472      * Template method which generates the method <code>hashCode()</code>.
473      *
474      * @return string with the <code>hashCode()</code> method definition in JAVA format
475      */
476     def protected generateHashCode() '''
477         «IF !genTO.hashCodeIdentifiers.empty»
478             @«Override.importedName»
479             public int hashCode() {
480                 final int prime = 31;
481                 int result = 1;
482                 «FOR property : genTO.hashCodeIdentifiers»
483                     «IF property.returnType.name.contains("[")»
484                     result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
485                     «ELSE»
486                     result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
487                     «ENDIF»
488                 «ENDFOR»
489                 return result;
490             }
491         «ENDIF»
492     '''
493
494     /**
495      * Template method which generates the method <code>equals()</code>.
496      *
497      * @return string with the <code>equals()</code> method definition in JAVA format
498      */
499     def protected generateEquals() '''
500         «IF !genTO.equalsIdentifiers.empty»
501             @«Override.importedName»
502             public boolean equals(java.lang.Object obj) {
503                 if (this == obj) {
504                     return true;
505                 }
506                 if (obj == null) {
507                     return false;
508                 }
509                 if (getClass() != obj.getClass()) {
510                     return false;
511                 }
512                 «type.name» other = («type.name») obj;
513                 «FOR property : genTO.equalsIdentifiers»
514                     «val fieldName = property.fieldName»
515                     «IF property.returnType.name.contains("[")»
516                     if (!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
517                     «ELSE»
518                     if (!«Objects.importedName».equals(«fieldName», other.«fieldName»)) {
519                     «ENDIF»
520                         return false;
521                     }
522                 «ENDFOR»
523                 return true;
524             }
525         «ENDIF»
526     '''
527
528     def GeneratedProperty getPropByName(String name) {
529         for (GeneratedProperty prop : allProperties) {
530             if (prop.name.equals(name)) {
531                 return prop;
532             }
533         }
534         return null;
535     }
536
537 }