Code refactoring
[yangtools.git] / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / yangtools / sal / java / api / generator / ClassTemplate.xtend
1 package org.opendaylight.yangtools.sal.java.api.generator
2
3 import java.util.List
4 import java.util.Map
5 import org.opendaylight.yangtools.binding.generator.util.TypeConstants
6 import org.opendaylight.yangtools.sal.binding.model.api.Constant
7 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
8 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
9 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
10 import org.opendaylight.yangtools.sal.binding.model.api.Type
11 import org.opendaylight.yangtools.binding.generator.util.Types
12 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
13
14 /**
15  * Template for generating JAVA class. 
16  */
17 class ClassTemplate {
18     
19     /**
20      * Generated transfer object for which class JAVA file is generated
21      */
22     val GeneratedTransferObject genTO
23     
24     /**
25      * Map of imports for this <code>genTO</code>.
26      */
27     val Map<String, String> imports
28     
29     /**
30      * List of generated property instances which represents class attributes.
31      */
32     val List<GeneratedProperty> fields
33     
34     /**
35      * List of enumeration which are generated as JAVA enum type.
36      */
37     val List<Enumeration> enums
38     
39     /**
40      * List of constant instances which are generated as JAVA public static final attributes.
41      */
42     val List<Constant> consts
43     
44     /**
45      * List of generated types which are enclosed inside <code>genType</code>
46      */
47     val List<GeneratedType> enclosedGeneratedTypes;
48         
49     /**
50      * Creates instance of this class with concrete <code>genTO</code>.
51      * 
52      * @param genTO generated transfer object which will be transformed to JAVA class source code
53      */
54     new(GeneratedTransferObject genTO) {
55         if (genTO == null) {
56             throw new IllegalArgumentException("Generated transfer object reference cannot be NULL!")
57         }
58         
59         this.genTO = genTO
60         this.imports = GeneratorUtil.createImports(genTO)
61         this.fields = genTO.properties
62         this.enums = genTO.enumerations
63         this.consts = genTO.constantDefinitions
64         this.enclosedGeneratedTypes = genTO.enclosedTypes
65     }
66     
67     /**
68      * Generates JAVA class source code (package name + class body).
69      * 
70      * @return string with JAVA class source code
71      */
72     def String generate() {
73         val body = generateBody(false)
74         val pkgAndImports = generatePkgAndImports
75         return pkgAndImports.toString + body.toString
76     }
77     
78     /**
79      * Generates JAVA class source code (class body only).
80      * 
81      * @return string with JAVA class body source code
82      */
83     def generateAsInnerClass() {
84         return generateBody(true)
85     }
86     
87     /**
88      * Template method which generates class body.
89      * 
90      * @param isInnerClass boolean value which specify if generated class is|isn't inner
91      * @return string with class source code in JAVA format
92      */
93     def private generateBody(boolean isInnerClass) '''
94         «genTO.comment.generateComment»
95         «generateClassDeclaration(isInnerClass)» {
96                 «generateInnerClasses»
97
98             «generateEnums»
99         
100             «generateConstants»
101         
102             «generateFields»
103         
104             «generateConstructor»
105         
106             «FOR field : fields SEPARATOR "\n"»
107                 «field.generateGetter»
108                 «IF !field.readOnly»
109                 
110                     «field.generateSetter»
111                 «ENDIF»
112             «ENDFOR»
113         
114             «generateHashCode»
115         
116             «generateEquals»
117         
118             «generateToString»
119         
120         }
121     '''
122     
123     
124     /**
125      * Template method which generates inner classes inside this interface.
126      * 
127      * @return string with the source code for inner classes in JAVA format
128      */
129     def private generateInnerClasses() '''
130         «IF !enclosedGeneratedTypes.empty»
131             «FOR innerClass : enclosedGeneratedTypes SEPARATOR "\n"»
132                 «IF (innerClass instanceof GeneratedTransferObject)»
133                     «val classTemplate = new ClassTemplate(innerClass as GeneratedTransferObject)»
134                     «classTemplate.generateAsInnerClass»
135                     
136                 «ENDIF»
137             «ENDFOR»
138         «ENDIF»
139     '''
140         
141     /**
142      * Template method which generates JAVA comments.
143      * 
144      * @param string with the comment for whole JAVA class
145      * @return string with comment in JAVA format
146      */
147     def private generateComment(String comment) '''
148         «IF comment != null && !comment.empty»
149             /*
150             «comment»
151             */
152         «ENDIF»
153     '''
154     
155     /**
156      * Template method which generates JAVA class declaration.
157      * 
158      * @param isInnerClass boolean value which specify if generated class is|isn't inner
159      * @return string with class declaration in JAVA format
160      */
161     def private generateClassDeclaration(boolean isInnerClass) '''
162         public«
163         IF (isInnerClass)»«
164             " static final "»«
165         ELSEIF (genTO.abstract)»«
166             " abstract "»«
167         ELSE»«
168             " "»«
169         ENDIF»class «genTO.name»«
170         IF (genTO.extends != null)»«
171             " extends "»«genTO.extends.resolveName»«
172         ENDIF»«
173         IF (!genTO.implements.empty)»«
174             " implements "»«
175             FOR type : genTO.implements SEPARATOR ", "»«
176                 type.resolveName»«
177             ENDFOR»«
178         ENDIF
179     »'''
180     
181     /**
182      * Template method which generates JAVA enum type.
183      * 
184      * @return string with inner enum source code in JAVA format
185      */
186     def private generateEnums() '''
187         «IF !enums.empty»
188             «FOR e : enums SEPARATOR "\n"»
189                 «val enumTemplate = new EnumTemplate(e)»
190                 «enumTemplate.generateAsInnerClass»
191             «ENDFOR»
192         «ENDIF»
193     '''
194     
195     /**
196      * Template method wich generates JAVA constants.
197      * 
198      * @return string with constants in JAVA format 
199      */
200     def private generateConstants() '''
201         «IF !consts.empty»
202             «FOR c : consts»
203                 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
204                     «val cValue = c.value»
205                     «IF cValue instanceof List<?>»
206                         «val cValues = cValue as List<?>»
207                         private static final List<Pattern> «Constants.MEMBER_PATTERN_LIST» = new ArrayList<Pattern>();
208                         public static final List<String> «TypeConstants.PATTERN_CONSTANT_NAME» = Arrays.asList(«
209                         FOR v : cValues SEPARATOR ", "»«
210                             IF v instanceof String»"«
211                                 v as String»"«
212                             ENDIF»«
213                         ENDFOR»);
214                         
215                         «generateStaticInicializationBlock»
216                     «ENDIF»
217                 «ELSE»
218                     public static final «c.type.resolveName» «c.name» = «c.value»;
219                 «ENDIF»
220             «ENDFOR»
221         «ENDIF»
222     '''
223     
224     /**
225      * Template method which generates JAVA static initialization block.
226      * 
227      * @return string with static initialization block in JAVA format
228      */
229     def private generateStaticInicializationBlock() '''
230         static {
231             for (String regEx : «TypeConstants.PATTERN_CONSTANT_NAME») {
232                 «Constants.MEMBER_PATTERN_LIST».add(Pattern.compile(regEx));
233             }
234         }
235     '''
236     
237     /**
238      * Template method which generates JAVA class attributes.
239      * 
240      * @return string with the class attributes in JAVA format
241      */
242     def private generateFields() '''
243         «IF !fields.empty»
244             «FOR f : fields»
245                 private «f.returnType.resolveName» «f.fieldName»;
246             «ENDFOR»
247         «ENDIF»
248     '''
249     
250     /**
251      * Template method which generates JAVA constructor(s).
252      * 
253      * @return string with the class constructor(s) in JAVA format
254      */
255     def private generateConstructor() '''
256         «val genTOTopParent = GeneratorUtil.getTopParrentTransportObject(genTO)»
257         «val properties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)»
258         «val propertiesAllParents = GeneratorUtil.getPropertiesOfAllParents(genTO)»
259         «IF !genTO.unionType»
260 «««            create constructor for every parent property
261             «IF genTOTopParent != genTO && genTOTopParent.unionType»
262                 «FOR parentProperty : propertiesAllParents SEPARATOR "\n"»
263                     «val parentPropertyAndProperties = properties + #[parentProperty]»
264                     «if (genTO.abstract) "protected" else "public"» «genTO.name»(«parentPropertyAndProperties.generateParameters») {
265                         super(«#[parentProperty].generateParameterNames»);
266                         «FOR property : properties»
267                             this.«property.fieldName» = «property.name»;
268                         «ENDFOR»
269                     }
270                 «ENDFOR»
271 «««            create one constructor
272             «ELSE»
273                 «val propertiesAll = propertiesAllParents + properties»
274                 «if (genTO.abstract) "protected" else "public"» «genTO.name»(«propertiesAll.generateParameters») {
275                     super(«propertiesAllParents.generateParameterNames()»);
276                     «FOR property : properties»
277                         this.«property.fieldName» = «property.fieldName»;
278                     «ENDFOR»
279                 }
280             «ENDIF»
281 «««        create constructor for every property
282         «ELSE»
283             «FOR property : properties SEPARATOR "\n"»
284                 «val propertyAndTopParentProperties = propertiesAllParents + #[property]»
285                 «if (genTO.abstract) "protected" else "public"» «genTO.name»(«propertyAndTopParentProperties.generateParameters») {
286                     super(«propertiesAllParents.generateParameterNames()»);
287                     this.«property.fieldName» = «property.fieldName»;
288                 }
289             «ENDFOR»
290         «ENDIF»
291     '''
292     
293     /**
294      * Template method which generates the getter method for <code>field</code>
295      * 
296      * @param field 
297      * generated property with data about field which is generated as the getter method
298      * @return string with the getter method source code in JAVA format 
299      */     
300     def private generateGetter(GeneratedProperty field) {
301         val prefix = if(field.returnType.equals(Types.typeForClass(Boolean))) "is" else "get"
302     '''
303         public «field.returnType.resolveName» «prefix»«field.name.toFirstUpper»() {
304             return «field.fieldName»;
305
306         }
307     '''
308     }
309     /**
310      * Template method which generates the setter method for <code>field</code>
311      * 
312      * @param field 
313      * generated property with data about field which is generated as the setter method
314      * @return string with the setter method source code in JAVA format 
315      */
316      def private generateSetter(GeneratedProperty field) '''
317         «val type = field.returnType.resolveName»
318         public void set«field.name.toFirstUpper»(«type» «field.fieldName») {
319             this.«field.fieldName» = «field.fieldName»;
320         }
321     '''
322     
323     /**
324      * Template method which generates method parameters with their types from <code>parameters</code>.
325      * 
326      * @param parameters
327      * group of generated property instances which are transformed to the method parameters
328      * @return string with the list of the method parameters with their types in JAVA format
329      */
330     def private generateParameters(Iterable<GeneratedProperty> parameters) '''«
331         IF !parameters.empty»«
332             FOR parameter : parameters SEPARATOR ", "»«
333                 parameter.returnType.resolveName» «parameter.fieldName»«
334             ENDFOR»«
335         ENDIF
336     »'''
337     
338     /**
339      * Template method which generates sequence of the names of the class attributes from <code>parameters</code>.
340      * 
341      * @param parameters 
342      * group of generated property instances which are transformed to the sequence of parameter names
343      * @return string with the list of the parameter names of the <code>parameters</code> 
344      */
345     def private generateParameterNames(Iterable<GeneratedProperty> parameters) '''«
346         IF !parameters.empty»«
347             FOR parameter : parameters SEPARATOR ", "»«
348                 parameter.fieldName»«
349             ENDFOR»«
350         ENDIF
351     »'''
352     
353     /**
354      * Template method which generates the method <code>hashCode()</code>.
355      * 
356      * @return string with the <code>hashCode()</code> method definition in JAVA format
357      */
358     def private generateHashCode() '''
359         «IF !genTO.hashCodeIdentifiers.empty»
360             @Override
361             public int hashCode() {
362                 final int prime = 31;
363                 int result = 1;
364                 «FOR property : genTO.hashCodeIdentifiers»
365                     result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
366                 «ENDFOR»
367                 return result;
368             }
369         «ENDIF»
370     '''
371     
372     /**
373      * Template method which generates the method <code>equals()</code>.
374      * 
375      * @return string with the <code>equals()</code> method definition in JAVA format     
376      */
377     def private generateEquals() '''
378         «IF !genTO.equalsIdentifiers.empty»
379             @Override
380             public boolean equals(java.lang.Object obj) {
381                 if (this == obj) {
382                     return true;
383                 }
384                 if (obj == null) {
385                     return false;
386                 }
387                 if (getClass() != obj.getClass()) {
388                     return false;
389                 }
390                 «genTO.name» other = («genTO.name») obj;
391                 «FOR property : genTO.equalsIdentifiers»
392                     «val fieldName = property.fieldName»
393                     if («fieldName» == null) {
394                         if (other.«fieldName» != null) {
395                             return false;
396                         }
397                     } else if(!«fieldName».equals(other.«fieldName»)) {
398                         return false;
399                     }
400                 «ENDFOR»
401                 return true;
402             }
403         «ENDIF»
404     '''
405     
406     /**
407      * Template method which generates the method <code>toString()</code>.
408      * 
409      * @return string with the <code>toString()</code> method definition in JAVA format     
410      */
411     def private generateToString() '''
412         «IF !genTO.toStringIdentifiers.empty»
413             @Override
414             public String toString() {
415                 StringBuilder builder = new StringBuilder();
416                 «val properties = genTO.toStringIdentifiers»
417                 builder.append("«genTO.name» [«properties.get(0).fieldName»=");
418                 builder.append(«properties.get(0).fieldName»);
419                 «FOR i : 1..<genTO.toStringIdentifiers.size»
420                     builder.append(", «properties.get(i).fieldName»=");
421                     builder.append(«properties.get(i).fieldName»);
422                 «ENDFOR»
423                 builder.append("]");
424                 return builder.toString();
425             }
426         «ENDIF»
427     '''
428     
429     /**
430      * Template method which generate package name line and import lines.
431      * 
432      * @result string with package and import lines in JAVA format
433      */
434     def private generatePkgAndImports() '''
435         package «genTO.packageName»;
436         
437         
438         «IF !imports.empty»
439             «FOR entry : imports.entrySet»
440                 import «entry.value».«entry.key»;
441             «ENDFOR»
442         «ENDIF»
443         
444     '''
445
446     /**
447      * Adds package to imports if it is necessary and returns necessary type name (with or without package name)
448      * 
449          * @param type JAVA <code>Type</code> 
450      * @return string with the type name (with or without package name)
451      */    
452     def private resolveName(Type type) {
453         GeneratorUtil.putTypeIntoImports(genTO, type, imports);
454         GeneratorUtil.getExplicitType(genTO, type, imports)
455     }
456     
457     def private fieldName(GeneratedProperty property) {
458         '''_«property.name»'''
459     }
460 }