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