BUG-1201: make yangtools compilable with java 8.
[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             «generateGetLength»
129
130             «generateGetRange»
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         «val added = other.remove(p)»
189         «genConstructor(p, other)»
190     «ENDFOR»
191
192     '''
193
194     def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
195     public «type.name»(«property.returnType.importedName + " " + property.name») {
196         «IF false == parentProperties.empty»
197             super(«parentProperties.asArguments»);
198         «ENDIF»
199             «generateRestrictions(type, property.fieldName.toString, property.returnType)»
200             this.«property.fieldName» = «property.name»;
201             «FOR p : other»
202             this.«p.fieldName» = null;
203             «ENDFOR»
204     }
205     '''
206
207     def protected copyConstructor() '''
208     /**
209      * Creates a copy from Source Object.
210      *
211      * @param source Source object
212      */
213     public «type.name»(«type.name» source) {
214         «IF false == parentProperties.empty»
215             super(source);
216         «ENDIF»
217         «FOR p : properties» 
218             this.«p.fieldName» = source.«p.fieldName»;
219         «ENDFOR»
220     }
221     '''
222
223     def protected parentConstructor() '''
224     /**
225      * Creates a new instance from «genTO.superType.importedName»
226      *
227      * @param source Source object
228      */
229     public «type.name»(«genTO.superType.importedName» source) {
230             super(source);
231     }
232     '''
233
234     def protected defaultInstance() '''
235         «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
236             «val prop = allProperties.get(0)»
237             «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
238             public static «genTO.name» getDefaultInstance(String defaultValue) {
239                 «IF "byte[]".equals(prop.returnType.name)»
240                     «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64(); 
241                     return new «genTO.name»(baseEncoding.decode(defaultValue));
242                 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
243                     return new «genTO.name»(defaultValue);
244                 «ELSEIF allProperties.size > 1»
245                     «bitsArgs»
246                 «ELSE»
247                     return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
248                 «ENDIF»
249             }
250             «ENDIF»
251         «ENDIF»
252     '''
253
254     def protected bitsArgs() '''
255         «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
256         if (!properties.contains(defaultValue)) {
257             throw new «IllegalArgumentException.importedName»("invalid default parameter");
258         }
259         int i = 0;
260         return new «genTO.name»(
261         «FOR prop : allProperties SEPARATOR ","»
262             properties.get(i++).equals(defaultValue) ? new «Boolean.importedName»("true") : null
263         «ENDFOR»
264         );
265     '''
266
267     def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
268         «FOR prop : properties SEPARATOR ","»
269             "«prop.name»"
270         «ENDFOR»
271     '''
272
273     /**
274      * Template method which generates JAVA class declaration.
275      * 
276      * @param isInnerClass boolean value which specify if generated class is|isn't inner
277      * @return string with class declaration in JAVA format
278      */
279     def protected generateClassDeclaration(boolean isInnerClass) '''
280         public«
281         IF (isInnerClass)»«
282             " static final "»«
283         ELSEIF (type.abstract)»«
284             " abstract "»«
285         ELSE»«
286             " "»«
287         ENDIF»class «type.name»«
288         IF (genTO.superType != null)»«
289             " extends "»«genTO.superType.importedName»«
290         ENDIF»
291         «IF (!type.implements.empty)»«
292             " implements "»«
293             FOR type : type.implements SEPARATOR ", "»«
294                 type.importedName»«
295             ENDFOR»«
296         ENDIF
297     »'''
298     
299     /**
300      * Template method which generates JAVA enum type.
301      * 
302      * @return string with inner enum source code in JAVA format
303      */
304     def protected enumDeclarations() '''
305         «IF !enums.empty»
306             «FOR e : enums SEPARATOR "\n"»
307                 «val enumTemplate = new EnumTemplate(e)»
308                 «enumTemplate.generateAsInnerClass»
309             «ENDFOR»
310         «ENDIF»
311     '''
312
313     def protected suidDeclaration() '''
314         «IF genTO.SUID != null»
315             private static final long serialVersionUID = «genTO.SUID.value»L; 
316         «ENDIF»
317     '''
318
319     /**
320      * Template method wich generates JAVA constants.
321      * 
322      * @return string with constants in JAVA format 
323      */
324     def protected constantsDeclarations() '''
325         «IF !consts.empty»
326             «FOR c : consts»
327                 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
328                     «val cValue = c.value»
329                     «IF cValue instanceof List<?>»
330                         «val cValues = cValue as List<?>»
331                         private static final «List.importedName»<«Pattern.importedName»> «Constants.MEMBER_PATTERN_LIST» = new «ArrayList.importedName»<«Pattern.importedName»>();
332                         public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «Arrays.importedName».asList(«
333                         FOR v : cValues SEPARATOR ", "»«
334                             IF v instanceof String»"«
335                                 v as String»"«
336                             ENDIF»«
337                         ENDFOR»);
338
339                         «generateStaticInicializationBlock»
340                     «ENDIF»
341                 «ELSE»
342                     public static final «c.type.importedName» «c.name» = «c.value»;
343                 «ENDIF»
344             «ENDFOR»
345         «ENDIF»
346     '''
347
348     /**
349      * Template method which generates JAVA static initialization block.
350      *
351      * @return string with static initialization block in JAVA format
352      */
353     def protected generateStaticInicializationBlock() '''
354         static {
355             for (String regEx : «TypeConstants.PATTERN_CONSTANT_NAME») {
356                 «Constants.MEMBER_PATTERN_LIST».add(Pattern.compile(regEx));
357             }
358         }
359     '''
360
361     /**
362      * Template method which generates JAVA class attributes.
363      *
364      * @return string with the class attributes in JAVA format
365      */
366     def protected generateFields() '''
367         «IF !properties.empty»
368             «FOR f : properties»
369                 «IF f.readOnly»final«ENDIF» private «f.returnType.importedName» «f.fieldName»;
370             «ENDFOR»
371         «ENDIF»
372     '''
373
374
375     /**
376      * Template method which generates the method <code>hashCode()</code>.
377      *
378      * @return string with the <code>hashCode()</code> method definition in JAVA format
379      */
380     def protected generateHashCode() '''
381         «IF !genTO.hashCodeIdentifiers.empty»
382             @Override
383             public int hashCode() {
384                 final int prime = 31;
385                 int result = 1;
386                 «FOR property : genTO.hashCodeIdentifiers»
387                     «IF property.returnType.name.contains("[")»
388                     result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
389                     «ELSE»
390                     result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
391                     «ENDIF»
392                 «ENDFOR»
393                 return result;
394             }
395         «ENDIF»
396     '''
397
398     /**
399      * Template method which generates the method <code>equals()</code>.
400      *
401      * @return string with the <code>equals()</code> method definition in JAVA format
402      */
403     def protected generateEquals() '''
404         «IF !genTO.equalsIdentifiers.empty»
405             @Override
406             public boolean equals(java.lang.Object obj) {
407                 if (this == obj) {
408                     return true;
409                 }
410                 if (obj == null) {
411                     return false;
412                 }
413                 if (getClass() != obj.getClass()) {
414                     return false;
415                 }
416                 «type.name» other = («type.name») obj;
417                 «FOR property : genTO.equalsIdentifiers»
418                     «val fieldName = property.fieldName»
419                     if («fieldName» == null) {
420                         if (other.«fieldName» != null) {
421                             return false;
422                         }
423                     «IF property.returnType.name.contains("[")»
424                     } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
425                     «ELSE»
426                     } else if(!«fieldName».equals(other.«fieldName»)) {
427                     «ENDIF»
428                         return false;
429                     }
430                 «ENDFOR»
431                 return true;
432             }
433         «ENDIF»
434     '''
435
436     def private generateGetLength() '''
437         «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
438             «val clazz = restrictions.lengthConstraints.iterator.next.min.class»
439             public static «List.importedName»<«Range.importedName»<«clazz.importedName»>> length() {
440                 final «List.importedName»<«Range.importedName»<«clazz.importedName»>> result = new «ArrayList.importedName»<>();
441                 «FOR r : restrictions.lengthConstraints»
442                     result.add(«Range.importedName».closed(new «clazz.importedName»("«r.min»"), new «clazz.importedName»("«r.max»")));
443                 «ENDFOR»
444                 return result;
445             }
446         «ENDIF»
447     '''
448
449     def private generateGetRange() '''
450         «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
451             «val clazz = restrictions.rangeConstraints.iterator.next.min.class»
452             public static «List.importedName»<«Range.importedName»<«clazz.importedName»>> range() {
453                 final «List.importedName»<«Range.importedName»<«clazz.importedName»>> result = new «ArrayList.importedName»<>();
454                 «FOR r : restrictions.rangeConstraints»
455                     result.add(«Range.importedName».closed(new «clazz.importedName»("«r.min»"), new «clazz.importedName»("«r.max»")));
456                 «ENDFOR»
457                 return result;
458             }
459         «ENDIF»
460     '''
461
462 }