BUG-1276: fixed generated union constructor
[yangtools.git] / code-generator / 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 java.util.List
11 import org.opendaylight.yangtools.binding.generator.util.TypeConstants
12 import org.opendaylight.yangtools.sal.binding.model.api.Constant
13 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
14 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
15 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
16 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
17 import java.util.ArrayList
18 import java.util.Collections\rimport java.util.Arrays
19 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
20 import com.google.common.collect.Range
21 import java.util.regex.Pattern
22 import com.google.common.io.BaseEncoding
23 import java.beans.ConstructorProperties
24 import com.google.common.collect.Lists
25
26 /**
27  * Template for generating JAVA class. 
28  */
29 class ClassTemplate extends BaseTemplate {
30
31     protected val List<GeneratedProperty> properties
32     protected val List<GeneratedProperty> finalProperties
33     protected val List<GeneratedProperty> parentProperties
34     protected val Iterable<GeneratedProperty> allProperties;
35     protected val Restrictions restrictions
36     
37     /**
38      * List of enumeration which are generated as JAVA enum type.
39      */
40     protected val List<Enumeration> enums
41     
42     /**
43      * List of constant instances which are generated as JAVA public static final attributes.
44      */
45     protected val List<Constant> consts
46     
47     /**
48      * List of generated types which are enclosed inside <code>genType</code>
49      */
50     protected val List<GeneratedType> enclosedGeneratedTypes;
51     
52     
53     protected val GeneratedTransferObject genTO;
54
55     /**
56      * Creates instance of this class with concrete <code>genType</code>.
57      * 
58      * @param genType generated transfer object which will be transformed to JAVA class source code
59      */
60     new(GeneratedTransferObject genType) {
61         super(genType)
62         this.genTO = genType
63         this.properties = genType.properties
64         this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
65         this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
66         this.restrictions = genType.restrictions
67
68         var List<GeneratedProperty> sorted = new ArrayList<GeneratedProperty>();
69         sorted.addAll(properties);
70         sorted.addAll(parentProperties);
71         Collections.sort(sorted, [p1, p2|
72             p1.name.compareTo(p2.name)
73         ]);
74
75         this.allProperties = sorted
76         this.enums = genType.enumerations
77         this.consts = genType.constantDefinitions
78         this.enclosedGeneratedTypes = genType.enclosedTypes
79     }
80
81
82     /**
83      * Generates JAVA class source code (class body only).
84      * 
85      * @return string with JAVA class body source code
86      */
87     def CharSequence generateAsInnerClass() {
88         return generateBody(true)
89     }
90
91
92     override protected body() {
93         generateBody(false);
94     }
95
96     /**
97      * Template method which generates class body.
98      * 
99      * @param isInnerClass boolean value which specify if generated class is|isn't inner
100      * @return string with class source code in JAVA format
101      */
102     def protected generateBody(boolean isInnerClass) '''
103         «type.comment.asJavadoc»
104         «generateClassDeclaration(isInnerClass)» {
105             «suidDeclaration»
106             «innerClassesDeclarations»
107             «enumDeclarations»
108             «constantsDeclarations»
109             «generateFields»
110
111             «constructors»
112             
113             «defaultInstance»
114
115             «FOR field : properties SEPARATOR "\n"»
116                 «field.getterMethod»
117                 «IF !field.readOnly»
118                     «field.setterMethod»
119                 «ENDIF»
120             «ENDFOR»
121
122             «generateHashCode»
123
124             «generateEquals»
125
126             «generateToString(genTO.toStringIdentifiers)»
127
128             «generateLengthMethod("length", genTO, genTO.importedName, "_length")»
129
130             «generateRangeMethod("range", genTO, genTO.importedName, "_range")»
131
132         }
133     '''
134
135
136     /**
137      * Template method which generates inner classes inside this interface.
138      * 
139      * @return string with the source code for inner classes in JAVA format
140      */
141     def protected innerClassesDeclarations() '''
142         «IF !enclosedGeneratedTypes.empty»
143             «FOR innerClass : enclosedGeneratedTypes SEPARATOR "\n"»
144                 «IF (innerClass instanceof GeneratedTransferObject)»
145                     «val classTemplate = new ClassTemplate(innerClass as GeneratedTransferObject)»
146                     «classTemplate.generateAsInnerClass»
147                     
148                 «ENDIF»
149             «ENDFOR»
150         «ENDIF»
151     '''
152     
153     
154     def protected constructors() '''
155         «IF genTO.unionType»
156             «genUnionConstructor»
157         «ELSE»
158             «allValuesConstructor»
159         «ENDIF»
160         «IF !allProperties.empty»
161             «copyConstructor»
162         «ENDIF»
163         «IF properties.empty && !parentProperties.empty »
164             «parentConstructor»
165         «ENDIF»
166     '''
167
168     def protected allValuesConstructor() '''
169     «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
170         @«ConstructorProperties.importedName»("value")
171     «ENDIF»
172     public «type.name»(«allProperties.asArgumentsDeclaration») {
173         «IF false == parentProperties.empty»
174             super(«parentProperties.asArguments»);
175         «ENDIF»
176         «FOR p : allProperties» 
177             «generateRestrictions(type, p.fieldName.toString, p.returnType)»
178         «ENDFOR»
179         «FOR p : properties» 
180             this.«p.fieldName» = «p.fieldName»;
181         «ENDFOR»
182     }
183     '''
184
185     def protected genUnionConstructor() '''
186     «FOR p : allProperties»
187         «val List<GeneratedProperty> other = new ArrayList(properties)»
188         «IF other.remove(p)»
189             «genConstructor(p, other)»
190         «ENDIF»
191     «ENDFOR»
192
193     '''
194
195     def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
196     public «type.name»(«property.returnType.importedName + " " + property.name») {
197         «IF false == parentProperties.empty»
198             super(«parentProperties.asArguments»);
199         «ENDIF»
200             «generateRestrictions(type, property.fieldName.toString, property.returnType)»
201             this.«property.fieldName» = «property.name»;
202             «FOR p : other»
203             this.«p.fieldName» = null;
204             «ENDFOR»
205     }
206     '''
207
208     def protected copyConstructor() '''
209     /**
210      * Creates a copy from Source Object.
211      *
212      * @param source Source object
213      */
214     public «type.name»(«type.name» source) {
215         «IF false == parentProperties.empty»
216             super(source);
217         «ENDIF»
218         «FOR p : properties» 
219             this.«p.fieldName» = source.«p.fieldName»;
220         «ENDFOR»
221     }
222     '''
223
224     def protected parentConstructor() '''
225     /**
226      * Creates a new instance from «genTO.superType.importedName»
227      *
228      * @param source Source object
229      */
230     public «type.name»(«genTO.superType.importedName» source) {
231             super(source);
232     }
233     '''
234
235     def protected defaultInstance() '''
236         «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
237             «val prop = allProperties.get(0)»
238             «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
239             public static «genTO.name» getDefaultInstance(String defaultValue) {
240                 «IF "byte[]".equals(prop.returnType.name)»
241                     «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64(); 
242                     return new «genTO.name»(baseEncoding.decode(defaultValue));
243                 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
244                     return new «genTO.name»(defaultValue);
245                 «ELSEIF allProperties.size > 1»
246                     «bitsArgs»
247                 «ELSE»
248                     return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
249                 «ENDIF»
250             }
251             «ENDIF»
252         «ENDIF»
253     '''
254
255     def protected bitsArgs() '''
256         «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
257         if (!properties.contains(defaultValue)) {
258             throw new «IllegalArgumentException.importedName»("invalid default parameter");
259         }
260         int i = 0;
261         return new «genTO.name»(
262         «FOR prop : allProperties SEPARATOR ","»
263             properties.get(i++).equals(defaultValue) ? new «Boolean.importedName»("true") : null
264         «ENDFOR»
265         );
266     '''
267
268     def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
269         «FOR prop : properties SEPARATOR ","»
270             "«prop.name»"
271         «ENDFOR»
272     '''
273
274     /**
275      * Template method which generates JAVA class declaration.
276      * 
277      * @param isInnerClass boolean value which specify if generated class is|isn't inner
278      * @return string with class declaration in JAVA format
279      */
280     def protected generateClassDeclaration(boolean isInnerClass) '''
281         public«
282         IF (isInnerClass)»«
283             " static final "»«
284         ELSEIF (type.abstract)»«
285             " abstract "»«
286         ELSE»«
287             " "»«
288         ENDIF»class «type.name»«
289         IF (genTO.superType != null)»«
290             " extends "»«genTO.superType.importedName»«
291         ENDIF»
292         «IF (!type.implements.empty)»«
293             " implements "»«
294             FOR type : type.implements SEPARATOR ", "»«
295                 type.importedName»«
296             ENDFOR»«
297         ENDIF
298     »'''
299     
300     /**
301      * Template method which generates JAVA enum type.
302      * 
303      * @return string with inner enum source code in JAVA format
304      */
305     def protected enumDeclarations() '''
306         «IF !enums.empty»
307             «FOR e : enums SEPARATOR "\n"»
308                 «val enumTemplate = new EnumTemplate(e)»
309                 «enumTemplate.generateAsInnerClass»
310             «ENDFOR»
311         «ENDIF»
312     '''
313
314     def protected suidDeclaration() '''
315         «IF genTO.SUID != null»
316             private static final long serialVersionUID = «genTO.SUID.value»L; 
317         «ENDIF»
318     '''
319
320     /**
321      * Template method wich generates JAVA constants.
322      * 
323      * @return string with constants in JAVA format 
324      */
325     def protected constantsDeclarations() '''
326         «IF !consts.empty»
327             «FOR c : consts»
328                 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
329                     «val cValue = c.value»
330                     «IF cValue instanceof List<?>»
331                         «val cValues = cValue as List<?>»
332                         private static final «List.importedName»<«Pattern.importedName»> «Constants.MEMBER_PATTERN_LIST» = new «ArrayList.importedName»<«Pattern.importedName»>();
333                         public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «Arrays.importedName».asList(«
334                         FOR v : cValues SEPARATOR ", "»«
335                             IF v instanceof String»"«
336                                 v as String»"«
337                             ENDIF»«
338                         ENDFOR»);
339
340                         «generateStaticInicializationBlock»
341                     «ENDIF»
342                 «ELSE»
343                     public static final «c.type.importedName» «c.name» = «c.value»;
344                 «ENDIF»
345             «ENDFOR»
346         «ENDIF»
347     '''
348
349     /**
350      * Template method which generates JAVA static initialization block.
351      *
352      * @return string with static initialization block in JAVA format
353      */
354     def protected generateStaticInicializationBlock() '''
355         static {
356             for (String regEx : «TypeConstants.PATTERN_CONSTANT_NAME») {
357                 «Constants.MEMBER_PATTERN_LIST».add(Pattern.compile(regEx));
358             }
359         }
360     '''
361
362     /**
363      * Template method which generates JAVA class attributes.
364      *
365      * @return string with the class attributes in JAVA format
366      */
367     def protected generateFields() '''
368         «IF restrictions != null»
369             «IF !(restrictions.lengthConstraints.empty)»
370                 «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
371                 private static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> _length;
372             «ENDIF»
373             «IF !(restrictions.rangeConstraints.empty)»
374                 «val numberClass = restrictions.rangeConstraints.iterator.next.min.class»
375                 private static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> _range;
376             «ENDIF»
377         «ENDIF»
378         «IF !properties.empty»
379             «FOR f : properties»
380                 «IF f.readOnly»final«ENDIF» private «f.returnType.importedName» «f.fieldName»;
381             «ENDFOR»
382         «ENDIF»
383     '''
384
385
386     /**
387      * Template method which generates the method <code>hashCode()</code>.
388      *
389      * @return string with the <code>hashCode()</code> method definition in JAVA format
390      */
391     def protected generateHashCode() '''
392         «IF !genTO.hashCodeIdentifiers.empty»
393             @Override
394             public int hashCode() {
395                 final int prime = 31;
396                 int result = 1;
397                 «FOR property : genTO.hashCodeIdentifiers»
398                     «IF property.returnType.name.contains("[")»
399                     result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
400                     «ELSE»
401                     result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
402                     «ENDIF»
403                 «ENDFOR»
404                 return result;
405             }
406         «ENDIF»
407     '''
408
409     /**
410      * Template method which generates the method <code>equals()</code>.
411      *
412      * @return string with the <code>equals()</code> method definition in JAVA format
413      */
414     def protected generateEquals() '''
415         «IF !genTO.equalsIdentifiers.empty»
416             @Override
417             public boolean equals(java.lang.Object obj) {
418                 if (this == obj) {
419                     return true;
420                 }
421                 if (obj == null) {
422                     return false;
423                 }
424                 if (getClass() != obj.getClass()) {
425                     return false;
426                 }
427                 «type.name» other = («type.name») obj;
428                 «FOR property : genTO.equalsIdentifiers»
429                     «val fieldName = property.fieldName»
430                     if («fieldName» == null) {
431                         if (other.«fieldName» != null) {
432                             return false;
433                         }
434                     «IF property.returnType.name.contains("[")»
435                     } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
436                     «ELSE»
437                     } else if(!«fieldName».equals(other.«fieldName»)) {
438                     «ENDIF»
439                         return false;
440                     }
441                 «ENDFOR»
442                 return true;
443             }
444         «ENDIF»
445     '''
446
447 }