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