Use StringBuilder fluently
[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 = \"").append(tempText).append("\">").append(tempText).append("</a>")
233
234         if (badEnding)
235             sb.append(lastChar)
236
237         return sb.toString
238     }
239
240     protected static def formatToParagraph(String inputText) {
241         val StringBuilder sb = new StringBuilder();
242         var StringBuilder lineBuilder = new StringBuilder();
243         var boolean isFirstElementOnNewLineEmptyChar = false;
244
245         var formattedText = WS_MATCHER.replaceFrom(inputText.encodeJavadocSymbols, SPACE)
246         formattedText = SPACES_PATTERN.matcher(formattedText).replaceAll(" ")
247
248         val StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true)
249         while (tokenizer.hasMoreTokens) {
250             val nextElement = tokenizer.nextToken
251
252             if (lineBuilder.length != 0 && lineBuilder.length + nextElement.length > 80) {
253                 if (lineBuilder.charAt(lineBuilder.length - 1) == SPACE) {
254                     lineBuilder.setLength(lineBuilder.length - 1)
255                 }
256                 if (lineBuilder.length != 0 && lineBuilder.charAt(0) == SPACE) {
257                     lineBuilder.deleteCharAt(0)
258                 }
259
260                 sb.append(lineBuilder).append(NEW_LINE)
261                 lineBuilder.setLength(0)
262
263                 if (nextElement == " ") {
264                     isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar;
265                 }
266             }
267             if (isFirstElementOnNewLineEmptyChar) {
268                 isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar
269             } else {
270                 lineBuilder.append(nextElement)
271             }
272         }
273
274         return sb.append(lineBuilder).append(NEW_LINE).toString
275     }
276
277     /**
278      * Template method which generates method parameters with their types from <code>parameters</code>.
279      *
280      * @param parameters
281      * list of parameter instances which are transformed to the method parameters
282      * @return string with the list of the method parameters with their types in JAVA format
283      */
284     def protected generateParameters(List<MethodSignature.Parameter> parameters) '''«
285         IF !parameters.empty»«
286             FOR parameter : parameters SEPARATOR ", "»«
287                 parameter.type.importedName» «parameter.name»«
288             ENDFOR»«
289         ENDIF
290     »'''
291
292     def protected emitConstant(Constant c) '''
293         «IF BindingMapping.QNAME_STATIC_FIELD_NAME.equals(c.name)»
294             «val entry = c.value as Entry<JavaTypeName, String>»
295             public static final «c.type.importedNonNull» «c.name» = «entry.key.importedName».«BindingMapping.MODULE_INFO_QNAMEOF_METHOD_NAME»("«entry.value»");
296         «ELSE»
297             public static final «c.type.importedName» «c.name» = «c.value»;
298         «ENDIF»
299     '''
300
301     def protected generateCheckers(GeneratedProperty field, Restrictions restrictions, Type actualType) '''
302        «IF restrictions.rangeConstraint.present»
303            «AbstractRangeGenerator.forType(actualType).generateRangeChecker(field.name.toFirstUpper,
304                restrictions.rangeConstraint.get, this)»
305        «ENDIF»
306        «IF restrictions.lengthConstraint.present»
307            «LengthGenerator.generateLengthChecker(field.fieldName, actualType, restrictions.lengthConstraint.get, this)»
308        «ENDIF»
309     '''
310
311     def protected checkArgument(GeneratedProperty property, Restrictions restrictions, Type actualType, String value) '''
312        «IF restrictions.getRangeConstraint.isPresent»
313            «IF actualType instanceof ConcreteType»
314                «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, value)»
315            «ELSE»
316                «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, value + ".getValue()")»
317            «ENDIF»
318        «ENDIF»
319        «val fieldName = property.fieldName»
320        «IF restrictions.getLengthConstraint.isPresent»
321            «IF actualType instanceof ConcreteType»
322                «LengthGenerator.generateLengthCheckerCall(fieldName, value)»
323            «ELSE»
324                «LengthGenerator.generateLengthCheckerCall(fieldName, value + ".getValue()")»
325            «ENDIF»
326        «ENDIF»
327
328        «val fieldUpperCase = fieldName.toUpperCase(Locale.ENGLISH)»
329        «FOR currentConstant : type.getConstantDefinitions»
330            «IF currentConstant.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)
331                && fieldUpperCase.equals(currentConstant.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length))»
332            «CODEHELPERS.importedName».checkPattern(value, «Constants.MEMBER_PATTERN_LIST»«fieldName», «Constants.MEMBER_REGEX_LIST»«fieldName»);
333            «ENDIF»
334        «ENDFOR»
335     '''
336
337     def protected hashCodeResult(Collection<? extends GeneratedProperty> properties) '''
338         final int prime = 31;
339         int result = 1;
340         «FOR property : properties»
341             result = prime * result + «property.importedUtilClass».hashCode(«property.fieldName»);
342         «ENDFOR»
343     '''
344
345     def protected final generateAnnotation(AnnotationType annotation) '''
346         @«annotation.importedName»
347         «IF annotation.parameters !== null && !annotation.parameters.empty»
348         (
349         «FOR param : annotation.parameters SEPARATOR ","»
350             «param.name»=«param.value»
351         «ENDFOR»
352         )
353         «ENDIF»
354     '''
355 }