Merge "Bug 1027: Decreased log level from INFO to DEBUG."
[mdsal.git] / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / yangtools / sal / java / api / generator / BaseTemplate.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 org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
11 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
12 import java.util.Map
13 import org.opendaylight.yangtools.sal.binding.model.api.Type
14 import org.opendaylight.yangtools.binding.generator.util.Types
15 import com.google.common.base.Splitter
16 import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
17 import com.google.common.collect.Range
18 import java.util.ArrayList
19 import java.util.List
20 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType
21 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
22 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
23 import java.util.Collection
24 import java.util.Arrays
25 import java.util.HashMap
26
27 abstract class BaseTemplate {
28
29     protected val GeneratedType type;
30     protected val Map<String, String> importMap;
31     static val paragraphSplitter = Splitter.on("\n\n").omitEmptyStrings();
32
33     new(GeneratedType _type) {
34         if (_type == null) {
35             throw new IllegalArgumentException("Generated type reference cannot be NULL!")
36         }
37         this.type = _type;
38         this.importMap = new HashMap<String,String>()
39     }
40
41     def packageDefinition() '''package «type.packageName»;'''
42
43     protected def getFullyQualifiedName() {
44         return type.fullyQualifiedName
45     }
46
47     final public def generate() {
48         val _body = body()
49         '''
50             «packageDefinition»
51             «imports»
52             
53             «_body»
54         '''.toString
55     }
56
57     protected def imports() ''' 
58         «IF !importMap.empty»
59             «FOR entry : importMap.entrySet»
60                 «IF entry.value != fullyQualifiedName»
61                     import «entry.value».«entry.key»;
62                 «ENDIF»
63             «ENDFOR»
64         «ENDIF»
65         
66     '''
67
68     protected abstract def CharSequence body();
69
70     // Helper patterns
71     final protected def fieldName(GeneratedProperty property) '''_«property.name»'''
72
73     final protected def propertyNameFromGetter(MethodSignature getter) {
74         var int prefix;
75         if (getter.name.startsWith("is")) {
76             prefix = 2
77         } else if (getter.name.startsWith("get")) {
78             prefix = 3
79         } else {
80             throw new IllegalArgumentException("Not a getter")
81         }
82         return getter.name.substring(prefix).toFirstLower;
83     }
84
85     /**
86      * Template method which generates the getter method for <code>field</code>
87      * 
88      * @param field 
89      * generated property with data about field which is generated as the getter method
90      * @return string with the getter method source code in JAVA format 
91      */
92     final protected def getterMethod(GeneratedProperty field) {
93         '''
94             public «field.returnType.importedName» «field.getterMethodName»() {
95                 return «field.fieldName»;
96             }
97         '''
98     }
99
100     final protected def getterMethodName(GeneratedProperty field) {
101         val prefix = if(field.returnType.equals(Types.BOOLEAN)) "is" else "get"
102         return '''«prefix»«field.name.toFirstUpper»'''
103     }
104
105     /**
106      * Template method which generates the setter method for <code>field</code>
107      * 
108      * @param field 
109      * generated property with data about field which is generated as the setter method
110      * @return string with the setter method source code in JAVA format 
111      */
112     final protected def setterMethod(GeneratedProperty field) '''
113         «val returnType = field.returnType.importedName»
114         public «type.name» set«field.name.toFirstUpper»(«returnType» value) {
115             this.«field.fieldName» = value;
116             return this;
117         }
118     '''
119
120     final protected def importedName(Type intype) {
121         GeneratorUtil.putTypeIntoImports(type, intype, importMap);
122         GeneratorUtil.getExplicitType(type, intype, importMap)
123     }
124
125     final protected def importedName(Class<?> cls) {
126         importedName(Types.typeForClass(cls))
127     }
128
129     /**
130      * Template method which generates method parameters with their types from <code>parameters</code>.
131      * 
132      * @param parameters
133      * group of generated property instances which are transformed to the method parameters
134      * @return string with the list of the method parameters with their types in JAVA format
135      */
136     def final protected asArgumentsDeclaration(Iterable<GeneratedProperty> parameters) '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.
137         returnType.importedName» «parameter.fieldName»«ENDFOR»«ENDIF»'''
138
139     /**
140      * Template method which generates sequence of the names of the class attributes from <code>parameters</code>.
141      * 
142      * @param parameters 
143      * group of generated property instances which are transformed to the sequence of parameter names
144      * @return string with the list of the parameter names of the <code>parameters</code> 
145      */
146     def final protected asArguments(Iterable<GeneratedProperty> parameters) '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.
147         fieldName»«ENDFOR»«ENDIF»'''
148
149     /**
150      * Template method which generates JAVA comments.
151      * 
152      * @param comment string with the comment for whole JAVA class
153      * @return string with comment in JAVA format
154      */
155     def protected CharSequence asJavadoc(String comment) {
156         if(comment == null) return '';
157         var txt = comment
158         if (txt.contains("*/")) {
159             txt = txt.replace("*/", "&#42;&#47;")
160         }
161         val paragraphs = paragraphSplitter.split(txt)
162
163         return '''
164             /**
165               «FOR p : paragraphs SEPARATOR "<p>"»
166                   «p»
167               «ENDFOR»
168             **/
169         '''
170     }
171
172     def generateRestrictions(Type type, String paramName, Type returnType) '''
173         «val boolean isArray = returnType.name.contains("[")»
174         «processRestrictions(type, paramName, returnType, isArray)»
175     '''
176
177     def generateRestrictions(GeneratedProperty field, String paramName) '''
178         «val Type type = field.returnType»
179         «IF type instanceof ConcreteType»
180             «processRestrictions(type, paramName, field.returnType, type.name.contains("["))»
181         «ELSEIF type instanceof GeneratedTransferObject»
182             «processRestrictions(type, paramName, field.returnType, isArrayType(type as GeneratedTransferObject))»
183         «ENDIF»
184     '''
185
186
187     private def processRestrictions(Type type, String paramName, Type returnType, boolean isArray) '''
188         «val restrictions = type.getRestrictions»
189         «IF restrictions !== null»
190             «IF !restrictions.lengthConstraints.empty»
191                 «generateLengthRestriction(type, restrictions, paramName, isArray,
192             !(returnType instanceof ConcreteType))»
193             «ENDIF»
194             «IF !restrictions.rangeConstraints.empty &&
195             ("java.lang".equals(returnType.packageName) || "java.math".equals(returnType.packageName))»
196                 «generateRangeRestriction(type, returnType, restrictions, paramName,
197             !(returnType instanceof ConcreteType))»
198             «ENDIF»
199         «ENDIF»
200     '''
201
202     def generateLengthRestriction(Type type, Restrictions restrictions, String paramName, boolean isArray,
203         boolean isNestedType) '''
204         if («paramName» != null) {
205             boolean isValidLength = false;
206             «List.importedName»<«Range.importedName»<«Integer.importedName»>> lengthConstraints = new «ArrayList.
207             importedName»<>(); 
208             «FOR r : restrictions.lengthConstraints»
209                 lengthConstraints.add(«Range.importedName».closed(«r.min», «r.max»));
210             «ENDFOR»
211             for («Range.importedName»<«Integer.importedName»> r : lengthConstraints) {
212                 «IF isArray»
213                     «IF isNestedType»
214                         if (r.contains(«paramName».getValue().length)) {
215                     «ELSE»
216                         if (r.contains(«paramName».length)) {
217                     «ENDIF»
218                 «ELSE»
219                     «IF isNestedType»
220                         if (r.contains(«paramName».getValue().length())) {
221                     «ELSE»
222                         if (r.contains(«paramName».length())) {
223                     «ENDIF»
224                 «ENDIF»
225                 isValidLength = true;
226                 }
227             }
228             if (!isValidLength) {
229                 throw new IllegalArgumentException(String.format("Invalid length: %s, expected: %s.", «paramName», lengthConstraints));
230             }
231         }
232     '''
233
234     def generateRangeRestriction(Type type, Type returnType, Restrictions restrictions, String paramName,
235         boolean isNestedType) '''
236         «val javaType = Class.forName(returnType.fullyQualifiedName)»
237         if («paramName» != null) {
238             boolean isValidRange = false;
239             «List.importedName»<«Range.importedName»<«javaType.importedName»>> rangeConstraints = new «ArrayList.
240             importedName»<>(); 
241             «FOR r : restrictions.rangeConstraints»
242                 rangeConstraints.add(«Range.importedName».closed(new «javaType.importedName»(«r.min.toQuote»), new «javaType.
243             importedName»(«r.max.toQuote»)));
244             «ENDFOR»
245             for («Range.importedName»<«javaType.importedName»> r : rangeConstraints) {
246                 «IF isNestedType»
247                     if (r.contains(«paramName».getValue())) {
248                 «ELSE»
249                     if (r.contains(«paramName»)) {
250                 «ENDIF»
251                 isValidRange = true;
252                 }
253             }
254             if (!isValidRange) {
255                 throw new IllegalArgumentException(String.format("Invalid range: %s, expected: %s.", «paramName», rangeConstraints));
256             }
257         }
258     '''
259
260     def protected generateToString(Collection<GeneratedProperty> properties) '''
261         «IF !properties.empty»
262             @Override
263             public «String.importedName» toString() {
264                 «StringBuilder.importedName» builder = new «StringBuilder.importedName»("«type.name» [");
265                 boolean first = true;
266
267                 «FOR property : properties»
268                     if («property.fieldName» != null) {
269                         if (first) {
270                             first = false;
271                         } else {
272                             builder.append(", ");
273                         }
274                         builder.append("«property.fieldName»=");
275                         «IF property.returnType.name.contains("[")»
276                             builder.append(«Arrays.importedName».toString(«property.fieldName»));
277                         «ELSE»
278                             builder.append(«property.fieldName»);
279                         «ENDIF»
280                      }
281                 «ENDFOR»
282                 return builder.append(']').toString();
283             }
284         «ENDIF»
285     '''
286
287     def GeneratedProperty getPropByName(GeneratedType gt, String name) {
288         for (GeneratedProperty prop : gt.properties) {
289             if (prop.name.equals(name)) {
290                 return prop;
291             }
292         }
293         return null;
294     }
295
296     def getRestrictions(Type type) {
297         var Restrictions restrictions = null
298         if (type instanceof ConcreteType) {
299             restrictions = (type as ConcreteType).restrictions
300         } else if (type instanceof GeneratedTransferObject) {
301             restrictions = (type as GeneratedTransferObject).restrictions
302         }
303         return restrictions
304     }
305
306     def boolean isArrayType(GeneratedTransferObject type) {
307         var isArray = false
308         val GeneratedTransferObject superType = type.findSuperType
309         val GeneratedProperty value = superType.getPropByName("value")
310         if (value != null && value.returnType.name.contains("[")) {
311             isArray = true
312         }
313         return isArray
314     }
315
316     def GeneratedTransferObject findSuperType(GeneratedTransferObject gto) {
317         var GeneratedTransferObject base = gto
318         var GeneratedTransferObject superType = base.superType
319         while (superType !== null) {
320             base = superType
321             superType = base.superType
322         }
323         return base;
324     }
325
326     def String toQuote(Object obj) {
327         return "\"" + obj.toString + "\"";
328     }
329
330     /**
331      * Template method which generates method parameters with their types from <code>parameters</code>.
332      * 
333      * @param parameters
334      * list of parameter instances which are transformed to the method parameters
335      * @return string with the list of the method parameters with their types in JAVA format
336      */
337     def protected generateParameters(List<MethodSignature.Parameter> parameters) '''«
338         IF !parameters.empty»«
339             FOR parameter : parameters SEPARATOR ", "»«
340                 parameter.type.importedName» «parameter.name»«
341             ENDFOR»«
342         ENDIF
343     »'''
344
345 }