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