Document YangModuleInfo(Provider)
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / 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.mdsal.binding.java.api.generator
9
10 import static extension org.opendaylight.mdsal.binding.generator.BindingGeneratorUtil.encodeAngleBrackets
11
12 import com.google.common.base.CharMatcher
13 import com.google.common.base.Splitter
14 import java.util.Collection
15 import java.util.List
16 import java.util.Locale
17 import java.util.Map.Entry
18 import java.util.StringTokenizer
19 import java.util.regex.Pattern
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.gaul.modernizer_maven_annotations.SuppressModernizer
22 import org.opendaylight.mdsal.binding.model.api.AnnotationType
23 import org.opendaylight.mdsal.binding.model.api.ConcreteType
24 import org.opendaylight.mdsal.binding.model.api.Constant
25 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
26 import org.opendaylight.mdsal.binding.model.api.GeneratedType
27 import org.opendaylight.mdsal.binding.model.api.JavaTypeName
28 import org.opendaylight.mdsal.binding.model.api.MethodSignature
29 import org.opendaylight.mdsal.binding.model.api.Restrictions
30 import org.opendaylight.mdsal.binding.model.api.Type
31 import org.opendaylight.mdsal.binding.model.api.TypeMemberComment
32 import org.opendaylight.mdsal.binding.model.ri.TypeConstants
33 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
34
35 @SuppressModernizer
36 abstract class BaseTemplate extends JavaFileTemplate {
37     static final char NEW_LINE = '\n'
38     static final char SPACE = ' '
39     static val WS_MATCHER = CharMatcher.anyOf("\n\t")
40     static val SPACES_PATTERN = Pattern.compile(" +")
41     static val NL_SPLITTER = Splitter.on(NEW_LINE)
42
43     new(GeneratedType type) {
44         super(type)
45     }
46
47     new(AbstractJavaGeneratedType javaType, GeneratedType type) {
48         super(javaType, type)
49     }
50
51     final def generate() {
52         val _body = body()
53         '''
54             package «type.packageName»;
55             «generateImportBlock»
56
57             «_body»
58         '''.toString
59     }
60
61     protected abstract def CharSequence body();
62
63     // Helper patterns
64     final protected def fieldName(GeneratedProperty property) {
65         "_" + property.name
66     }
67
68     /**
69      * Template method which generates the getter method for <code>field</code>
70      *
71      * @param field
72      * generated property with data about field which is generated as the getter method
73      * @return string with the getter method source code in JAVA format
74      */
75     protected def getterMethod(GeneratedProperty field) '''
76         «val methodName = field.getterMethodName»
77         public «field.returnType.importedName» «methodName»() {
78             «val fieldName = field.fieldName»
79             «IF field.returnType.name.endsWith("[]")»
80             return «fieldName» == null ? null : «fieldName».clone();
81             «ELSE»
82             return «fieldName»;
83             «ENDIF»
84         }
85     '''
86
87     final protected def getterMethodName(GeneratedProperty field) {
88         return '''«BindingMapping.GETTER_PREFIX»«field.name.toFirstUpper»'''
89     }
90
91     /**
92      * Template method which generates the setter method for <code>field</code>
93      *
94      * @param field
95      * generated property with data about field which is generated as the setter method
96      * @return string with the setter method source code in JAVA format
97      */
98     final protected def setterMethod(GeneratedProperty field) '''
99         «val returnType = field.returnType.importedName»
100         public «type.name» set«field.name.toFirstUpper»(«returnType» value) {
101             this.«field.fieldName» = value;
102             return this;
103         }
104     '''
105
106     /**
107      * Template method which generates method parameters with their types from <code>parameters</code>.
108      *
109      * @param parameters
110      * group of generated property instances which are transformed to the method parameters
111      * @return string with the list of the method parameters with their types in JAVA format
112      */
113     def final protected asArgumentsDeclaration(Iterable<GeneratedProperty> parameters) '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.
114         returnType.importedName» «parameter.fieldName»«ENDFOR»«ENDIF»'''
115
116     /**
117      * Template method which generates method parameters with their types from <code>parameters</code>, annotating them
118      * with {@link NonNull}.
119      *
120      * @param parameters group of generated property instances which are transformed to the method parameters
121      * @return string with the list of the method parameters with their types in JAVA format
122      */
123     def final protected asNonNullArgumentsDeclaration(Iterable<GeneratedProperty> parameters) '''«IF !parameters.empty»
124         «FOR parameter : parameters SEPARATOR ", "»«parameter.returnType.importedNonNull» «parameter
125         .fieldName»«ENDFOR»«ENDIF»'''
126
127     /**
128      * Template method which generates sequence of the names of the class attributes from <code>parameters</code>.
129      *
130      * @param parameters
131      * group of generated property instances which are transformed to the sequence of parameter names
132      * @return string with the list of the parameter names of the <code>parameters</code>
133      */
134     def final protected asArguments(Collection<GeneratedProperty> parameters) '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.
135         fieldName»«ENDFOR»«ENDIF»'''
136
137     /**
138      * Template method which generates JAVA comments.
139      *
140      * @param comment string with the comment for whole JAVA class
141      * @return string with comment in JAVA format
142      */
143     def final protected asJavadoc(TypeMemberComment comment) {
144         if (comment === null) {
145             return ''
146         }
147         return wrapToDocumentation('''
148            «comment.contractDescription»
149
150            «comment.referenceDescription.formatReference»
151
152            «comment.typeSignature»
153         ''')
154     }
155
156     def static String wrapToDocumentation(String text) {
157         if (text.empty)
158             return ""
159
160         val StringBuilder sb = new StringBuilder().append("/**\n")
161         for (String t : NL_SPLITTER.split(text)) {
162             sb.append(" *")
163             if (!t.isEmpty()) {
164                 sb.append(SPACE).append(t)
165             }
166             sb.append(NEW_LINE)
167         }
168         sb.append(" */")
169
170         return sb.toString
171     }
172
173     def protected String formatDataForJavaDoc(GeneratedType type) {
174         val sb = new StringBuilder()
175         val comment = type.comment
176         if (comment !== null) {
177             sb.append(comment.javadoc)
178         }
179
180         appendSnippet(sb, type)
181
182         return '''
183             «IF sb.length != 0»
184             «sb.toString»
185             «ENDIF»
186         '''.toString
187     }
188
189     def protected String formatDataForJavaDoc(GeneratedType type, String additionalComment) {
190         val comment = type.comment
191         if (comment === null) {
192             return '''
193                 «additionalComment»
194             '''
195         }
196
197         val sb = new StringBuilder().append(comment.javadoc)
198         appendSnippet(sb, type)
199
200         sb.append(NEW_LINE)
201         .append(NEW_LINE)
202         .append(NEW_LINE)
203         .append(additionalComment)
204
205         return '''
206             «sb.toString»
207         '''
208     }
209
210     def static formatReference(String reference) '''
211         «IF reference !== null»
212             <pre>
213                 <code>
214                     «reference.encodeAngleBrackets.formatToParagraph»
215                 </code>
216             </pre>
217
218         «ENDIF»
219     '''
220
221     def asLink(String text) {
222         val StringBuilder sb = new StringBuilder()
223         var tempText = text
224         var char lastChar = SPACE
225         var boolean badEnding = false
226
227         if (text.endsWith('.') || text.endsWith(':') || text.endsWith(',')) {
228             tempText = text.substring(0, text.length - 1)
229             lastChar = text.charAt(text.length - 1)
230             badEnding = true
231         }
232         sb.append("<a href = \"")
233         sb.append(tempText)
234         sb.append("\">")
235         sb.append(tempText)
236         sb.append("</a>")
237
238         if(badEnding)
239             sb.append(lastChar)
240
241         return sb.toString
242     }
243
244     protected static def formatToParagraph(String inputText) {
245         val StringBuilder sb = new StringBuilder();
246         var StringBuilder lineBuilder = new StringBuilder();
247         var boolean isFirstElementOnNewLineEmptyChar = false;
248
249         var formattedText = WS_MATCHER.replaceFrom(inputText.encodeJavadocSymbols, SPACE)
250         formattedText = SPACES_PATTERN.matcher(formattedText).replaceAll(" ")
251
252         val StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true)
253         while (tokenizer.hasMoreTokens) {
254             val nextElement = tokenizer.nextToken
255
256             if (lineBuilder.length != 0 && lineBuilder.length + nextElement.length > 80) {
257                 if (lineBuilder.charAt(lineBuilder.length - 1) == SPACE) {
258                     lineBuilder.setLength(lineBuilder.length - 1)
259                 }
260                 if (lineBuilder.length != 0 && lineBuilder.charAt(0) == SPACE) {
261                     lineBuilder.deleteCharAt(0)
262                 }
263
264                 sb.append(lineBuilder).append(NEW_LINE)
265                 lineBuilder.setLength(0)
266
267                 if (nextElement == " ") {
268                     isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar;
269                 }
270             }
271             if (isFirstElementOnNewLineEmptyChar) {
272                 isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar
273             } else {
274                 lineBuilder.append(nextElement)
275             }
276         }
277
278         return sb.append(lineBuilder).append(NEW_LINE).toString
279     }
280
281     /**
282      * Template method which generates method parameters with their types from <code>parameters</code>.
283      *
284      * @param parameters
285      * list of parameter instances which are transformed to the method parameters
286      * @return string with the list of the method parameters with their types in JAVA format
287      */
288     def protected generateParameters(List<MethodSignature.Parameter> parameters) '''«
289         IF !parameters.empty»«
290             FOR parameter : parameters SEPARATOR ", "»«
291                 parameter.type.importedName» «parameter.name»«
292             ENDFOR»«
293         ENDIF
294     »'''
295
296     def protected emitConstant(Constant c) '''
297         «IF BindingMapping.QNAME_STATIC_FIELD_NAME.equals(c.name)»
298             «val entry = c.value as Entry<JavaTypeName, String>»
299             public static final «c.type.importedNonNull» «c.name» = «entry.key.importedName».«BindingMapping.MODULE_INFO_QNAMEOF_METHOD_NAME»("«entry.value»");
300         «ELSE»
301             public static final «c.type.importedName» «c.name» = «c.value»;
302         «ENDIF»
303     '''
304
305     def protected generateCheckers(GeneratedProperty field, Restrictions restrictions, Type actualType) '''
306        «IF restrictions.rangeConstraint.present»
307            «AbstractRangeGenerator.forType(actualType).generateRangeChecker(field.name.toFirstUpper,
308                restrictions.rangeConstraint.get, this)»
309        «ENDIF»
310        «IF restrictions.lengthConstraint.present»
311            «LengthGenerator.generateLengthChecker(field.fieldName, actualType, restrictions.lengthConstraint.get, this)»
312        «ENDIF»
313     '''
314
315     def protected checkArgument(GeneratedProperty property, Restrictions restrictions, Type actualType, String value) '''
316        «IF restrictions.getRangeConstraint.isPresent»
317            «IF actualType instanceof ConcreteType»
318                «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, value)»
319            «ELSE»
320                «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, value + ".getValue()")»
321            «ENDIF»
322        «ENDIF»
323        «val fieldName = property.fieldName»
324        «IF restrictions.getLengthConstraint.isPresent»
325            «IF actualType instanceof ConcreteType»
326                «LengthGenerator.generateLengthCheckerCall(fieldName, value)»
327            «ELSE»
328                «LengthGenerator.generateLengthCheckerCall(fieldName, value + ".getValue()")»
329            «ENDIF»
330        «ENDIF»
331
332        «val fieldUpperCase = fieldName.toUpperCase(Locale.ENGLISH)»
333        «FOR currentConstant : type.getConstantDefinitions»
334            «IF currentConstant.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)
335                && fieldUpperCase.equals(currentConstant.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length))»
336            «CODEHELPERS.importedName».checkPattern(value, «Constants.MEMBER_PATTERN_LIST»«fieldName», «Constants.MEMBER_REGEX_LIST»«fieldName»);
337            «ENDIF»
338        «ENDFOR»
339     '''
340
341     def protected hashCodeResult(Collection<? extends GeneratedProperty> properties) '''
342         final int prime = 31;
343         int result = 1;
344         «FOR property : properties»
345             result = prime * result + «property.importedUtilClass».hashCode(«property.fieldName»);
346         «ENDFOR»
347     '''
348
349     def protected final generateAnnotation(AnnotationType annotation) '''
350         @«annotation.importedName»
351         «IF annotation.parameters !== null && !annotation.parameters.empty»
352         (
353         «FOR param : annotation.parameters SEPARATOR ","»
354             «param.name»=«param.value»
355         «ENDFOR»
356         )
357         «ENDIF»
358     '''
359 }