2 * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.mdsal.binding.javav2.java.api.generator.util;
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 com.google.common.collect.Iterables;
16 import com.google.common.collect.Lists;
17 import java.util.List;
18 import java.util.StringTokenizer;
19 import org.opendaylight.mdsal.binding.javav2.generator.util.Types;
20 import org.opendaylight.mdsal.binding.javav2.model.api.AccessModifier;
21 import org.opendaylight.mdsal.binding.javav2.model.api.ConcreteType;
22 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedProperty;
23 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject;
24 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
25 import org.opendaylight.mdsal.binding.javav2.model.api.MethodSignature;
26 import org.opendaylight.mdsal.binding.javav2.model.api.Restrictions;
27 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
28 import org.opendaylight.mdsal.binding.javav2.model.api.TypeMember;
29 import org.opendaylight.yangtools.concepts.Builder;
30 import org.opendaylight.yangtools.yang.model.api.Module;
32 public final class TextTemplateUtil {
34 public static final String DOT = ".";
35 public static final String PATTERN_CONSTANT_NAME = "PATTERN_CONSTANTS";
37 private static final char NEW_LINE = '\n';
38 private static final String UNDERSCORE = "_";
39 private static final CharMatcher NEWLINE_OR_TAB = CharMatcher.anyOf("\n\t");
40 private static final CharMatcher NL_MATCHER = CharMatcher.is(NEW_LINE);
41 private static final CharMatcher AMP_MATCHER = CharMatcher.is('&');
42 private static final CharMatcher GT_MATCHER = CharMatcher.is('>');
43 private static final CharMatcher LT_MATCHER = CharMatcher.is('<');
44 private static final Splitter NL_SPLITTER = Splitter.on(NL_MATCHER);
46 private TextTemplateUtil() {
47 throw new UnsupportedOperationException("Util class");
51 * Makes start of getter name LowerCase
53 * @param s getter name without prefix
54 * @return getter name starting in LowerCase
56 public static String toFirstLower(final String s) {
57 return s != null && s.length() != 0 ? Character.isLowerCase(s.charAt(0)) ? s : s.length() == 1 ?
58 s.toLowerCase() : s.substring(0, 1).toLowerCase() + s.substring(1) : s;
62 * Wraps text as documentation, used in enum description
64 * @param text text for wrapping
65 * @return wrapped text
67 public static String wrapToDocumentation(final String text) {
71 final StringBuilder sb = new StringBuilder(NEW_LINE);
74 final Iterable<String> lineSplitText = NL_SPLITTER.split(text);
75 for (final String t : lineSplitText) {
89 * Returns formatted Javadoc, based on type
91 * @return formatted Javadoc, based on type
93 public static String formatDataForJavaDocBuilder(final String typeName) {
94 final StringBuilder stringBuilder = new StringBuilder();
95 stringBuilder.append("Class that builds {@link ")
97 .append("} instances.")
101 return stringBuilder.toString();
105 * Returns formatted Javadoc with possible additional comment, based on type
107 * @param additionalComment
108 * @return formatted Javadoc with possible additional comment, based on type
110 public static String formatDataForJavaDoc(final GeneratedType type, final String additionalComment) {
111 final StringBuilder javaDoc = new StringBuilder();
112 if (type.getDescription().isPresent()) {
113 javaDoc.append(type.getDescription())
118 javaDoc.append(additionalComment);
119 return javaDoc.toString();
123 * Returns properties names in formatted string
125 * @return properties names in formatted string
127 //FIXME: this needs further clarification in future patch
128 public static String valueForBits(final List<GeneratedProperty> properties) {
129 return String.join(",", Lists.transform(properties, TextTemplateUtil::fieldName));
133 * Returns formatted type description
135 * @return formatted type description
137 public static String formatDataForJavaDoc(final GeneratedType type) {
138 final String description = type.getDescription().isPresent() ? type.getDescription().get() : "";
139 return encodeJavadocSymbols(description);
143 * Returns parameter name, based on given Type
146 * @return parameter name, based on given Type
148 public static String paramValue(final Type returnType, final String paramName) {
149 if (returnType instanceof ConcreteType) {
152 return paramName + ".getValue()";
157 * Template method which generates JAVA comments. InterfaceTemplate
159 * @param comment comment string with the comment for whole JAVA class
160 * @return string with comment in JAVA format
162 public static String asJavadoc(final String comment) {
163 if (comment == null) {
166 return wrapToDocumentation(formatToParagraph(comment.trim(), 0));
169 private static String formatDataForJavaDoc(final TypeMember type, final String additionalComment) {
170 final StringBuilder javaDoc = new StringBuilder();
171 if (type.getComment() != null && !type.getComment().isEmpty()) {
172 javaDoc.append(formatToParagraph(type.getComment(), 0))
177 javaDoc.append(additionalComment);
178 return wrapToDocumentation(javaDoc.toString());
182 * Returns related Javadoc
183 * @param methodSignature
184 * @return related Javadoc
186 public static String getJavaDocForInterface(final MethodSignature methodSignature) {
187 if (methodSignature.getReturnType() == Types.VOID) {
190 final StringBuilder javaDoc = new StringBuilder();
191 javaDoc.append("@return ")
192 .append(asCode(methodSignature.getReturnType().getFullyQualifiedName()))
194 .append(asCode(propertyNameFromGetter(methodSignature)))
196 .append(asCode("null"))
197 .append(" if not present");
198 return formatDataForJavaDoc(methodSignature, javaDoc.toString());
201 private static String asCode(final String text) {
202 return "<code>" + text + "</code>";
206 * Encodes angle brackets in yang statement description
207 * @param description description of a yang statement which is used to generate javadoc comments
208 * @return string with encoded angle brackets
210 public static String encodeAngleBrackets(String description) {
211 if (description != null) {
212 description = LT_MATCHER.replaceFrom(description, "<");
213 description = GT_MATCHER.replaceFrom(description, ">");
219 * Returns collection of properties as formatted String
221 * @return generated properties as formatted String
223 public static String propsAsArgs(final Iterable<GeneratedProperty> properties) {
224 return String.join(",", Iterables.transform(properties, prop -> "\"" + prop.getName() + "\""));
228 * Returns properties as formatted String
231 * @return Properties as formatted String
233 public static String propsAsList(final Iterable<GeneratedProperty> properties, final String booleanName) {
234 return String.join(",", Iterables.transform(properties,
235 prop -> "properties.get(i++).equals(defaultValue) ? " + booleanName + ".TRUE : null"));
239 * Extracts available restrictions from given type
241 * @return restrictions from given type
243 public static Restrictions getRestrictions(final Type currentType) {
244 Restrictions restrictions = null;
245 if (currentType instanceof ConcreteType) {
246 restrictions = ((ConcreteType) currentType).getRestrictions();
247 } else if (currentType instanceof GeneratedTransferObject) {
248 restrictions = ((GeneratedTransferObject) currentType).getRestrictions();
254 * sets fieldname according to property for return type
255 * method(type fieldname)
257 * @param property type from getter
259 public static String fieldName(final GeneratedProperty property) {
260 final String name = Preconditions.checkNotNull(property.getName());
261 return UNDERSCORE.concat(name);
265 * Template method which generates sequence of the names of the class attributes
267 * @param parameters group of generated property instances which are transformed to the sequence of parameter names
268 * @return string with the list of the parameter names
270 public static String asArguments(final List<GeneratedProperty> parameters) {
271 return String.join(", ", Lists.transform(parameters, TextTemplateUtil::fieldName));
275 * Helper method for building getter
277 * @param field property name
278 * @return getter for propery
280 public static String getterMethodName(final GeneratedProperty field) {
281 final Type type = Preconditions.checkNotNull(field.getReturnType());
282 final String name = Preconditions.checkNotNull(field.getName());
283 final String prefix = Types.BOOLEAN.equals(type) ? "is" : "get";
284 return prefix.concat(toFirstUpper(name));
288 * Returns built setter method body
291 * @param returnTypeName
292 * @return built setter method body
294 public static String setterMethod(final GeneratedProperty field, final String typeName, final String returnTypeName) {
295 final StringBuilder stringBuilder = new StringBuilder();
296 stringBuilder.append("public ")
299 .append(toFirstUpper(field.getName()))
301 .append(returnTypeName)
302 .append(" value) {\n this.")
303 .append(fieldName(field))
304 .append(" = value;\n return this;\n}\n");
305 return stringBuilder.toString();
309 * Returns simple name of underlying class
310 * @return Simple name of underlying class
312 public static String getSimpleNameForBuilder() {
313 return Builder.class.getSimpleName();
317 * Makes start of getter name uppercase
319 * @param s getter name without prefix
320 * @return getter name starting in uppercase
322 public static String toFirstUpper(final String s) {
323 return s != null && s.length() != 0 ? Character.isUpperCase(s.charAt(0)) ? s : s.length() == 1 ?
324 s.toUpperCase() : s.substring(0, 1).toUpperCase() + s.substring(1) : s;
328 * Cuts prefix from getter name
330 * @param getter getter name
331 * @return getter name without prefix
333 public static String propertyNameFromGetter(final MethodSignature getter) {
334 final String name = Preconditions.checkNotNull(getter.getName());
336 if (name.startsWith("is")) {
338 } else if (name.startsWith("get")) {
343 return toFirstLower(name.substring(prefix));
347 * Returns list of properties as formatted String
349 * @return formatted property list as String
351 public static String getPropertyList(final List<GeneratedProperty> properties) {
352 return String.join(", ", Lists.transform(properties, prop -> "base." + getterMethodName(prop) + "()"));
356 * util method for unionTemplateBuilderTemplate
357 * @return string with clarification for javadoc
359 public static String getClarification() {
360 final StringBuilder clarification = new StringBuilder();
361 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")
362 .append("In some cases it is very difficult to automate it since there can be unions such as (uint32 - uint16), or (string - uint32).\n")
364 .append("The reason behind putting it under src/main/java is:\n")
365 .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")
366 .append("loss of user code.\n")
368 return clarification.toString();
372 * Returns source path as String
374 * @return formatted String source path
376 public static String getSourcePath(final Module module) {
377 return "/".concat(module.getModuleSourcePath().replace(java.io.File.separatorChar, '/'));
381 * util method for unionTemplateBuilderTemplate
382 * @return needed access modifier
384 public static String getAccessModifier(final AccessModifier modifier) {
386 case PUBLIC: return "public ";
387 case PROTECTED: return "protected ";
388 case PRIVATE: return "private ";
394 * @param text Content of tag description
395 * @param nextLineIndent Number of spaces from left side default is 12
396 * @return formatted description
398 private static String formatToParagraph(final String text, final int nextLineIndent) {
399 if (Strings.isNullOrEmpty(text)) {
402 boolean isFirstElementOnNewLineEmptyChar = false;
403 final StringBuilder sb = new StringBuilder();
404 final StringBuilder lineBuilder = new StringBuilder();
405 final String lineIndent = Strings.repeat(" ", nextLineIndent);
406 final String textToFormat = NEWLINE_OR_TAB.removeFrom(encodeJavadocSymbols(text));
407 final String formattedText = textToFormat.replaceAll(" +", " ");
408 final StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true);
410 while (tokenizer.hasMoreElements()) {
411 final String nextElement = tokenizer.nextElement().toString();
413 if (lineBuilder.length() + nextElement.length() > 80) {
414 // Trim trailing whitespace
415 for (int i = lineBuilder.length() - 1; i >= 0 && lineBuilder.charAt(i) != ' '; --i) {
416 lineBuilder.setLength(i);
418 // Trim leading whitespace
419 while (lineBuilder.charAt(0) == ' ') {
420 lineBuilder.deleteCharAt(0);
422 sb.append(lineBuilder).append('\n');
423 lineBuilder.setLength(0);
425 if (nextLineIndent > 0) {
426 sb.append(lineIndent);
429 if (" ".equals(nextElement)) {
430 isFirstElementOnNewLineEmptyChar = true;
433 if (isFirstElementOnNewLineEmptyChar) {
434 isFirstElementOnNewLineEmptyChar = false;
436 lineBuilder.append(nextElement);
439 return sb.append(lineBuilder).append('\n').toString();
442 private static String encodeJavadocSymbols(final String description) {
443 if (description == null || description.isEmpty()) {
446 final String ret = description.replace("*/", "*/");
447 return AMP_MATCHER.replaceFrom(ret, "&");