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