Implemented build of default instance for union types.
[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         val paragraphs = paragraphSplitter.split(comment)
150
151         return '''
152             /**
153               «FOR p : paragraphs SEPARATOR "<p>"»
154                   «p»
155               «ENDFOR»
156             **/
157         '''
158     }
159
160     def generateRestrictions(Type type, String paramName, Type returnType) '''
161         «val boolean isArray = returnType.name.contains("[")»
162         «processRestrictions(type, paramName, returnType, isArray)»
163     '''
164
165     def generateRestrictions(GeneratedProperty field, String paramName) '''
166         «val Type type = field.returnType»
167         «IF type instanceof ConcreteType»
168             «processRestrictions(type, paramName, field.returnType, type.name.contains("["))»
169         «ELSEIF type instanceof GeneratedTransferObject»
170             «processRestrictions(type, paramName, field.returnType, isArrayType(type as GeneratedTransferObject))»
171         «ENDIF»
172     '''
173
174
175     private def processRestrictions(Type type, String paramName, Type returnType, boolean isArray) '''
176         «val restrictions = type.getRestrictions»
177         «IF restrictions !== null»
178             «IF !restrictions.lengthConstraints.empty»
179                 «generateLengthRestriction(type, restrictions, paramName, isArray,
180             !(returnType instanceof ConcreteType))»
181             «ENDIF»
182             «IF !restrictions.rangeConstraints.empty &&
183             ("java.lang".equals(returnType.packageName) || "java.math".equals(returnType.packageName))»
184                 «generateRangeRestriction(type, returnType, restrictions, paramName,
185             !(returnType instanceof ConcreteType))»
186             «ENDIF»
187         «ENDIF»
188     '''
189
190     def generateLengthRestriction(Type type, Restrictions restrictions, String paramName, boolean isArray,
191         boolean isNestedType) '''
192         if («paramName» != null) {
193             boolean isValidLength = false;
194             «List.importedName»<«Range.importedName»<«Integer.importedName»>> lengthConstraints = new «ArrayList.
195             importedName»<>(); 
196             «FOR r : restrictions.lengthConstraints»
197                 lengthConstraints.add(«Range.importedName».closed(«r.min», «r.max»));
198             «ENDFOR»
199             for («Range.importedName»<«Integer.importedName»> r : lengthConstraints) {
200                 «IF isArray»
201                     «IF isNestedType»
202                         if (r.contains(«paramName».getValue().length)) {
203                     «ELSE»
204                         if (r.contains(«paramName».length)) {
205                     «ENDIF»
206                 «ELSE»
207                     «IF isNestedType»
208                         if (r.contains(«paramName».getValue().length())) {
209                     «ELSE»
210                         if (r.contains(«paramName».length())) {
211                     «ENDIF»
212                 «ENDIF»
213                 isValidLength = true;
214                 }
215             }
216             if (!isValidLength) {
217                 throw new IllegalArgumentException(String.format("Invalid length: {}, expected: {}.", «paramName», lengthConstraints));
218             }
219         }
220     '''
221
222     def generateRangeRestriction(Type type, Type returnType, Restrictions restrictions, String paramName,
223         boolean isNestedType) '''
224         «val javaType = Class.forName(returnType.fullyQualifiedName)»
225         if («paramName» != null) {
226             boolean isValidRange = false;
227             «List.importedName»<«Range.importedName»<«javaType.importedName»>> rangeConstraints = new «ArrayList.
228             importedName»<>(); 
229             «FOR r : restrictions.rangeConstraints»
230                 rangeConstraints.add(«Range.importedName».closed(new «javaType.importedName»(«r.min.toQuote»), new «javaType.
231             importedName»(«r.max.toQuote»)));
232             «ENDFOR»
233             for («Range.importedName»<«javaType.importedName»> r : rangeConstraints) {
234                 «IF isNestedType»
235                     if (r.contains(«paramName».getValue())) {
236                 «ELSE»
237                     if (r.contains(«paramName»)) {
238                 «ENDIF»
239                 isValidRange = true;
240                 }
241             }
242             if (!isValidRange) {
243                 throw new IllegalArgumentException(String.format("Invalid range: %s, expected: %s.", «paramName», rangeConstraints));
244             }
245         }
246     '''
247
248     def protected generateToString(Collection<GeneratedProperty> properties) '''
249         «IF !properties.empty»
250             @Override
251             public String toString() {
252                 StringBuilder builder = new StringBuilder();
253                 builder.append("«type.name» [«properties.get(0).fieldName»=");
254                 «IF properties.get(0).returnType.name.contains("[")»
255                     builder.append(«Arrays.importedName».toString(«properties.get(0).fieldName»));
256                 «ELSE»
257                     builder.append(«properties.get(0).fieldName»);
258                 «ENDIF»
259                 «FOR i : 1..<properties.size»
260                     builder.append(", «properties.get(i).fieldName»=");
261                     «IF properties.get(i).returnType.name.contains("[")»
262                         builder.append(«Arrays.importedName».toString(«properties.get(i).fieldName»));
263                     «ELSE»
264                         builder.append(«properties.get(i).fieldName»);
265                     «ENDIF»
266                 «ENDFOR»
267                 builder.append("]");
268                 return builder.toString();
269             }
270         «ENDIF»
271     '''
272
273     def GeneratedProperty getPropByName(GeneratedType gt, String name) {
274         for (GeneratedProperty prop : gt.properties) {
275             if (prop.name.equals(name)) {
276                 return prop;
277             }
278         }
279         return null;
280     }
281
282     def GeneratedProperty getPropByName(Collection<GeneratedProperty> props, String name) {
283         for (GeneratedProperty prop : props) {
284             if (prop.name.equals(name)) {
285                 return prop;
286             }
287         }
288         return null;
289     }
290
291     def getRestrictions(Type type) {
292         var Restrictions restrictions = null
293         if (type instanceof ConcreteType) {
294             restrictions = (type as ConcreteType).restrictions
295         } else if (type instanceof GeneratedTransferObject) {
296             restrictions = (type as GeneratedTransferObject).restrictions
297         }
298         return restrictions
299     }
300
301     def boolean isArrayType(GeneratedTransferObject type) {
302         var isArray = false
303         val GeneratedTransferObject superType = type.findSuperType
304         val GeneratedProperty value = superType.getPropByName("value")
305         if (value != null && value.returnType.name.contains("[")) {
306             isArray = true
307         }
308         return isArray
309     }
310
311     def GeneratedTransferObject findSuperType(GeneratedTransferObject gto) {
312         var GeneratedTransferObject base = gto
313         var GeneratedTransferObject superType = base.superType
314         while (superType !== null) {
315             base = superType
316             superType = base.superType
317         }
318         return base;
319     }
320
321     def String toQuote(Object obj) {
322         return "\"" + obj.toString + "\"";
323     }
324
325     /**
326      * Template method which generates method parameters with their types from <code>parameters</code>.
327      * 
328      * @param parameters
329      * list of parameter instances which are transformed to the method parameters
330      * @return string with the list of the method parameters with their types in JAVA format
331      */
332     def protected generateParameters(List<MethodSignature.Parameter> parameters) '''«
333         IF !parameters.empty»«
334             FOR parameter : parameters SEPARATOR ", "»«
335                 parameter.type.importedName» «parameter.name»«
336             ENDFOR»«
337         ENDIF
338     »'''
339
340 }