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