Fix generating of description for operations
[mdsal.git] / binding2 / mdsal-binding2-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / javav2 / java / api / generator / util / TextTemplateUtil.java
1 /*
2  * Copyright (c) 2017 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
9 package org.opendaylight.mdsal.binding.javav2.java.api.generator.util;
10
11 import com.google.common.base.CharMatcher;
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Splitter;
14 import com.google.common.base.Strings;
15 import java.text.SimpleDateFormat;
16 import java.util.Date;
17 import java.util.LinkedList;
18 import java.util.List;
19 import java.util.StringTokenizer;
20 import org.opendaylight.mdsal.binding.javav2.generator.util.Types;
21 import org.opendaylight.mdsal.binding.javav2.model.api.AccessModifier;
22 import org.opendaylight.mdsal.binding.javav2.model.api.ConcreteType;
23 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedProperty;
24 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject;
25 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
26 import org.opendaylight.mdsal.binding.javav2.model.api.MethodSignature;
27 import org.opendaylight.mdsal.binding.javav2.model.api.Restrictions;
28 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
29 import org.opendaylight.mdsal.binding.javav2.model.api.TypeMember;
30 import org.opendaylight.yangtools.concepts.Builder;
31 import org.opendaylight.yangtools.yang.model.api.Module;
32
33 public final class TextTemplateUtil {
34
35     public static final String DOT = ".";
36     public static final String PATTERN_CONSTANT_NAME = "PATTERN_CONSTANTS";
37
38     private static final char NEW_LINE = '\n';
39     private static final String COMMA = ",";
40     private static final String UNDERSCORE = "_";
41     private static final CharMatcher NEWLINE_OR_TAB = CharMatcher.anyOf("\n\t");
42     private static final CharMatcher NL_MATCHER = CharMatcher.is(NEW_LINE);
43     private static final CharMatcher AMP_MATCHER = CharMatcher.is('&');
44     private static final CharMatcher GT_MATCHER = CharMatcher.is('>');
45     private static final CharMatcher LT_MATCHER = CharMatcher.is('<');
46     private static final Splitter NL_SPLITTER = Splitter.on(NL_MATCHER);
47
48     private TextTemplateUtil() {
49         throw new UnsupportedOperationException("Util class");
50     }
51
52     /**
53      * Makes start of getter name LowerCase
54      *
55      * @param s getter name without prefix
56      * @return getter name starting in LowerCase
57      */
58     public static String toFirstLower(final String s) {
59         return s != null && s.length() != 0 ? (Character.isLowerCase(s.charAt(0)) ? s : (s.length() == 1 ?
60                 s.toLowerCase() : s.substring(0, 1).toLowerCase() + s.substring(1))) : s;
61     }
62
63     /**
64      * Wraps text as documentation, used in enum description
65      *
66      * @param text text for wrapping
67      * @return wrapped text
68      */
69     public static String wrapToDocumentation(final String text) {
70         if (text.isEmpty()) {
71             return "";
72         }
73         final StringBuilder sb = new StringBuilder(NEW_LINE);
74         sb.append("/**");
75         sb.append(NEW_LINE);
76         final Iterable<String> lineSplitText = NL_SPLITTER.split(text);
77         for (final String t : lineSplitText) {
78             if (!t.isEmpty()) {
79                 sb.append(" *");
80                 sb.append(" ");
81                 sb.append(t);
82                 sb.append(NEW_LINE);
83             }
84         }
85         sb.append(" */");
86         sb.append(NEW_LINE);
87         return sb.toString();
88     }
89
90     /**
91      * Returns formatted Javadoc, based on type
92      * @param typeName
93      * @return formatted Javadoc, based on type
94      */
95     public static String formatDataForJavaDocBuilder(final String typeName) {
96         final StringBuilder stringBuilder = new StringBuilder();
97         stringBuilder.append("Class that builds {@link ")
98                 .append(typeName)
99                 .append("} instances.")
100                 .append(NEW_LINE)
101                 .append("@see ")
102                 .append(typeName);
103         return stringBuilder.toString();
104     }
105
106     /**
107      * Returns formatted Javadoc with possible additional comment, based on type
108      * @param type
109      * @param additionalComment
110      * @return formatted Javadoc with possible additional comment, based on type
111      */
112     public static String formatDataForJavaDoc(final GeneratedType type, final String additionalComment) {
113         final StringBuilder javaDoc = new StringBuilder();
114         if (type.getDescription().isPresent()) {
115             javaDoc.append(type.getDescription())
116                     .append(NEW_LINE)
117                     .append(NEW_LINE)
118                     .append(NEW_LINE);
119         }
120         javaDoc.append(additionalComment);
121         return javaDoc.toString();
122     }
123
124     /**
125      * Returns properties names in formatted string
126      * @param properties
127      * @return properties names in formatted string
128      */
129     //FIXME: this needs further clarification in future patch
130     public static String valueForBits(final List<GeneratedProperty> properties) {
131         final List<String> strings = new LinkedList<>();
132         for (final GeneratedProperty property : properties) {
133             strings.add(fieldName(property));
134         }
135         return String.join(",", strings);
136     }
137
138     /**
139      * Returns formatted type description
140      * @param type
141      * @return formatted type description
142      */
143     public static String formatDataForJavaDoc(final GeneratedType type) {
144         final String description = type.getDescription().isPresent() ? type.getDescription().get() : "";
145         return encodeJavadocSymbols(description);
146     }
147
148     /**
149      * Returns parameter name, based on given Type
150      * @param returnType
151      * @param paramName
152      * @return parameter name, based on given Type
153      */
154     public static String paramValue(final Type returnType, final String paramName) {
155         if (returnType instanceof ConcreteType) {
156             return paramName;
157         } else {
158             return paramName + ".getValue()";
159         }
160     }
161
162     /**
163      * Template method which generates JAVA comments. InterfaceTemplate
164      *
165      * @param comment comment string with the comment for whole JAVA class
166      * @return string with comment in JAVA format
167      */
168     public static String asJavadoc(final String comment) {
169         if (comment == null) {
170             return "";
171         }
172         return wrapToDocumentation(formatToParagraph(comment.trim(), 0));
173     }
174
175     private static String formatDataForJavaDoc(final TypeMember type, final String additionalComment) {
176         final StringBuilder javaDoc = new StringBuilder();
177         if (type.getComment() != null && !type.getComment().isEmpty()) {
178             javaDoc.append(formatToParagraph(type.getComment(), 0))
179                     .append(NEW_LINE)
180                     .append(NEW_LINE)
181                     .append(NEW_LINE);
182         }
183         javaDoc.append(additionalComment);
184         return wrapToDocumentation(javaDoc.toString());
185     }
186
187     /**
188      * Returns related Javadoc
189      * @param methodSignature
190      * @return related Javadoc
191      */
192     public static String getJavaDocForInterface(final MethodSignature methodSignature) {
193         if (methodSignature.getReturnType() == Types.VOID) {
194             return "";
195         }
196         final StringBuilder javaDoc = new StringBuilder();
197         javaDoc.append("@return ")
198                 .append(asCode(methodSignature.getReturnType().getFullyQualifiedName()))
199                 .append(" ")
200                 .append(asCode(propertyNameFromGetter(methodSignature)))
201                 .append(", or ")
202                 .append(asCode("null"))
203                 .append(" if not present");
204         return formatDataForJavaDoc(methodSignature, javaDoc.toString());
205     }
206
207     private static String asCode(final String text) {
208         return "<code>" + text + "</code>";
209     }
210
211     /**
212      * Encodes angle brackets in yang statement description
213      * @param description description of a yang statement which is used to generate javadoc comments
214      * @return string with encoded angle brackets
215      */
216     public static String encodeAngleBrackets(String description) {
217         if (description != null) {
218             description = LT_MATCHER.replaceFrom(description, "&lt;");
219             description = GT_MATCHER.replaceFrom(description, "&gt;");
220         }
221         return description;
222     }
223
224     /**
225      * Returns collection of properties as formatted String
226      * @param properties
227      * @return generated properties as formatted String
228      */
229     public static String propsAsArgs(final Iterable<GeneratedProperty> properties) {
230         final List<String> strings = new LinkedList<>();
231         if (properties.iterator().hasNext()) {
232             for (final GeneratedProperty property : properties) {
233                 final StringBuilder stringBuilder = new StringBuilder();
234                 stringBuilder.append("\"")
235                         .append(property.getName())
236                         .append("\"");
237                 strings.add(stringBuilder.toString());
238             }
239         }
240         return String.join(",", strings);
241     }
242
243     /**
244      * Returns properties as formatted String
245      * @param properties
246      * @param booleanName
247      * @return Properties as formatted String
248      */
249     public static String propsAsList(final Iterable<GeneratedProperty> properties, final String booleanName) {
250         final List<String> strings = new LinkedList<>();
251         if (properties.iterator().hasNext()) {
252             for (final GeneratedProperty property : properties) {
253                 final StringBuilder stringBuilder = new StringBuilder();
254                 stringBuilder.append("properties.get(i++).equals(defaultValue) ? ")
255                         .append(booleanName)
256                         .append(".TRUE : null");
257                 strings.add(stringBuilder.toString());
258             }
259         }
260         return String.join(",", strings);
261     }
262
263     /**
264      * Extracts available restrictions from given type
265      * @param currentType
266      * @return restrictions from given type
267      */
268     public static Restrictions getRestrictions(final Type currentType) {
269         Restrictions restrictions = null;
270         if (currentType instanceof ConcreteType) {
271             restrictions = ((ConcreteType) currentType).getRestrictions();
272         } else if (currentType instanceof GeneratedTransferObject) {
273             restrictions = ((GeneratedTransferObject) currentType).getRestrictions();
274         }
275         return restrictions;
276     }
277
278     /**
279      * sets fieldname according to property for return type
280      * method(type fieldname)
281      *
282      * @param property type from getter
283      */
284     public static String fieldName(final GeneratedProperty property) {
285         final String name = Preconditions.checkNotNull(property.getName());
286         return UNDERSCORE.concat(name);
287     }
288
289     /**
290      * Template method which generates sequence of the names of the class attributes
291      *
292      * @param parameters group of generated property instances which are transformed to the sequence of parameter names
293      * @return string with the list of the parameter names
294      */
295     public static String asArguments(final List<GeneratedProperty> parameters) {
296         final List<String> strings = new LinkedList<>();
297         if (!parameters.isEmpty()) {
298             for (final GeneratedProperty parameter : parameters) {
299                 strings.add((fieldName(parameter)));
300             }
301         }
302         return String.join(", ", strings);
303     }
304
305     /**
306      * Helper method for building getter
307      *
308      * @param field property name
309      * @return getter for propery
310      */
311     public static String getterMethodName(final GeneratedProperty field) {
312         final Type type = Preconditions.checkNotNull(field.getReturnType());
313         final String name = Preconditions.checkNotNull(field.getName());
314         final String prefix = Types.BOOLEAN.equals(type) ? "is" : "get";
315         return prefix.concat(toFirstUpper(name));
316     }
317
318     /**
319      * Returns built setter method body
320      * @param field
321      * @param typeName
322      * @param returnTypeName
323      * @return built setter method body
324      */
325     public static String setterMethod(final GeneratedProperty field, final String typeName, final String returnTypeName) {
326         final StringBuilder stringBuilder = new StringBuilder();
327         stringBuilder.append("public ")
328                 .append(typeName)
329                 .append(" set")
330                 .append(toFirstUpper(field.getName()))
331                 .append("(")
332                 .append(returnTypeName)
333                 .append(" value) {\n    this.")
334                 .append(fieldName(field))
335                 .append(" = value;\n    return this;\n}\n");
336         return stringBuilder.toString();
337     }
338
339     /**
340      * Returns simple name of underlying class
341      * @return Simple name of underlying class
342      */
343     public static String getSimpleNameForBuilder() {
344         return Builder.class.getSimpleName();
345     }
346
347     /**
348      * Makes start of getter name uppercase
349      *
350      * @param s getter name without prefix
351      * @return getter name starting in uppercase
352      */
353     public static String toFirstUpper(final String s) {
354         return s != null && s.length() != 0 ? (Character.isUpperCase(s.charAt(0)) ? s : (s.length() == 1 ?
355                 s.toUpperCase() : s.substring(0, 1).toUpperCase() + s.substring(1))) : s;
356     }
357
358     /**
359      * Cuts prefix from getter name
360      *
361      * @param getter getter name
362      * @return getter name without prefix
363      */
364     public static String propertyNameFromGetter(final MethodSignature getter) {
365         final String name = Preconditions.checkNotNull(getter.getName());
366         int prefix;
367         if (name.startsWith("is")) {
368             prefix = 2;
369         } else if (name.startsWith("get")) {
370             prefix = 3;
371         } else {
372             prefix = 0;
373         }
374         return toFirstLower(name.substring(prefix));
375     }
376
377     /**
378      * Returns list of properties as formatted String
379      * @param properties
380      * @return formatted property list as String
381      */
382     public static String getPropertyList(final List<GeneratedProperty> properties) {
383         final List<String> strings = new LinkedList<>();
384         if (!properties.isEmpty()) {
385             for (final GeneratedProperty property : properties) {
386                 final StringBuilder stringBuilder = new StringBuilder();
387                 stringBuilder.append("base.")
388                         .append(getterMethodName(property))
389                         .append("()");
390                 strings.add(stringBuilder.toString());
391             }
392         }
393         return String.join(", ", strings);
394     }
395
396     /**
397      * util method for unionTemplateBuilderTemplate
398      * @return string with clarification for javadoc
399      */
400     public static String getClarification() {
401         final StringBuilder clarification = new StringBuilder();
402         clarification.append("The purpose of generated class in src/main/java for Union types is to create new instances of unions from a string representation.\n")
403                 .append("In some cases it is very difficult to automate it since there can be unions such as (uint32 - uint16), or (string - uint32).\n")
404                 .append("\n")
405                 .append("The reason behind putting it under src/main/java is:\n")
406                 .append("This class is generated in form of a stub and needs to be finished by the user. This class is generated only once to prevent\n")
407                 .append("loss of user code.\n")
408                 .append("\n");
409         return clarification.toString();
410     }
411
412     /**
413      * Returns revision Date as String
414      * @param revision
415      * @return formatted Revision as String
416      */
417     public static String getFormattedRevision(final Date revision) {
418         final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
419         return simpleDateFormat.format(revision);
420     }
421
422     /**
423      * Returns source path as String
424      * @param module
425      * @return formatted String source path
426      */
427     public static String getSourcePath(final Module module) {
428         return "/".concat(module.getModuleSourcePath().replace(java.io.File.separatorChar, '/'));
429     }
430
431     /**
432      * util method for unionTemplateBuilderTemplate
433      * @return needed access modifier
434      */
435     public static String getAccessModifier(final AccessModifier modifier) {
436         switch (modifier) {
437             case PUBLIC: return "public ";
438             case PROTECTED: return "protected ";
439             case PRIVATE: return "private ";
440             default: return "";
441         }
442     }
443
444     /**
445      * @param text Content of tag description
446      * @param nextLineIndent Number of spaces from left side default is 12
447      * @return formatted description
448      */
449     private static String formatToParagraph(final String text, final int nextLineIndent) {
450         if (Strings.isNullOrEmpty(text)) {
451             return "";
452         }
453         boolean isFirstElementOnNewLineEmptyChar = false;
454         final StringBuilder sb = new StringBuilder();
455         final StringBuilder lineBuilder = new StringBuilder();
456         final String lineIndent = Strings.repeat(" ", nextLineIndent);
457         final String textToFormat = NEWLINE_OR_TAB.removeFrom(text);
458         final String formattedText = textToFormat.replaceAll(" +", " ");
459         final StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true);
460
461         while (tokenizer.hasMoreElements()) {
462             final String nextElement = tokenizer.nextElement().toString();
463
464             if (lineBuilder.length() + nextElement.length() > 80) {
465                 // Trim trailing whitespace
466                 for (int i = lineBuilder.length() - 1; i >= 0 && lineBuilder.charAt(i) != ' '; --i) {
467                     lineBuilder.setLength(i);
468                 }
469                 // Trim leading whitespace
470                 while (lineBuilder.charAt(0) == ' ') {
471                     lineBuilder.deleteCharAt(0);
472                 }
473                 sb.append(lineBuilder).append('\n');
474                 lineBuilder.setLength(0);
475
476                 if (nextLineIndent > 0) {
477                     sb.append(lineIndent);
478                 }
479
480                 if (" ".equals(nextElement)) {
481                     isFirstElementOnNewLineEmptyChar = true;
482                 }
483             }
484             if (isFirstElementOnNewLineEmptyChar) {
485                 isFirstElementOnNewLineEmptyChar = false;
486             } else {
487                 lineBuilder.append(nextElement);
488             }
489         }
490         return sb.append(lineBuilder).append('\n').toString();
491     }
492
493     private static String encodeJavadocSymbols(final String description) {
494         if (description == null || description.isEmpty()) {
495             return description;
496         }
497         final String ret = description.replace("*/", "&#42;&#47;");
498         return AMP_MATCHER.replaceFrom(ret, "&amp;");
499     }
500 }