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