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