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.Optional;
19 import java.util.StringTokenizer;
20 import java.util.function.Function;
21 import java.util.regex.Pattern;
22 import org.opendaylight.mdsal.binding.javav2.generator.util.Types;
23 import org.opendaylight.mdsal.binding.javav2.model.api.AccessModifier;
24 import org.opendaylight.mdsal.binding.javav2.model.api.ConcreteType;
25 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedProperty;
26 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject;
27 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
28 import org.opendaylight.mdsal.binding.javav2.model.api.MethodSignature;
29 import org.opendaylight.mdsal.binding.javav2.model.api.Restrictions;
30 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
31 import org.opendaylight.mdsal.binding.javav2.model.api.TypeMember;
32 import org.opendaylight.yangtools.concepts.Builder;
33 import org.opendaylight.yangtools.yang.model.api.Module;
35 public final class TextTemplateUtil {
37 public static final String DOT = ".";
38 public static final String PATTERN_CONSTANT_NAME = "PATTERN_CONSTANTS";
40 private static final char NEW_LINE = '\n';
41 private static final String UNDERSCORE = "_";
42 private static final CharMatcher NEWLINE_OR_TAB = CharMatcher.anyOf("\n\t");
43 private static final CharMatcher NL_MATCHER = CharMatcher.is(NEW_LINE);
44 private static final CharMatcher AMP_MATCHER = CharMatcher.is('&');
45 private static final CharMatcher GT_MATCHER = CharMatcher.is('>');
46 private static final CharMatcher LT_MATCHER = CharMatcher.is('<');
47 private static final Splitter NL_SPLITTER = Splitter.on(NL_MATCHER);
49 private static final Pattern TAIL_COMMENT_PATTERN = Pattern.compile("*/", Pattern.LITERAL);
50 private static final Pattern MULTIPLE_SPACES_PATTERN = Pattern.compile(" +");
52 private TextTemplateUtil() {
53 throw new UnsupportedOperationException("Util class");
57 * Makes start of getter name LowerCase
59 * @param s getter name without prefix
60 * @return getter name starting in LowerCase
62 public static String toFirstLower(final String s) {
63 return s != null && s.length() != 0 ? Character.isLowerCase(s.charAt(0)) ? s : s.length() == 1 ?
64 s.toLowerCase() : s.substring(0, 1).toLowerCase() + s.substring(1) : s;
68 * Wraps text as documentation, used in enum description
70 * @param text text for wrapping
71 * @return wrapped text
73 public static String wrapToDocumentation(final String text) {
77 final StringBuilder sb = new StringBuilder(NEW_LINE);
80 for (final String t : NL_SPLITTER.split(text)) {
93 * Returns formatted Javadoc, based on type
94 * @param typeName given type name
95 * @return formatted Javadoc, based on type
97 public static String formatDataForJavaDocBuilder(final String typeName) {
98 final StringBuilder stringBuilder = new StringBuilder();
99 stringBuilder.append("Class that builds {@link ")
101 .append("} instances.")
105 return stringBuilder.toString();
109 * Returns formatted Javadoc with possible additional comment, based on type
110 * @param type given type
111 * @param additionalComment additional comment to format
112 * @return formatted Javadoc with possible additional comment, based on type
114 public static String formatDataForJavaDoc(final GeneratedType type, final String additionalComment) {
115 final StringBuilder javaDoc = new StringBuilder();
116 if (type.getDescription().isPresent()) {
117 javaDoc.append(type.getDescription())
122 javaDoc.append(additionalComment);
123 return javaDoc.toString();
127 * Returns properties names in formatted string
128 * @param properties list of given properties
129 * @return properties names in formatted string
131 //FIXME: this needs further clarification in future patch
132 public static String valueForBits(final List<GeneratedProperty> properties) {
133 return String.join(",", Lists.transform(properties, TextTemplateUtil::fieldName));
137 * Returns formatted type description
138 * @param type given type
139 * @return formatted type description
141 public static String formatDataForJavaDoc(final GeneratedType type) {
142 return type.getDescription().map(TextTemplateUtil::encodeJavadocSymbols).orElse("");
146 * Returns parameter name, based on given Type
147 * @param returnType given type
148 * @param paramName parameter name
149 * @return parameter name, based on given Type
151 public static String paramValue(final Type returnType, final String paramName) {
152 return returnType instanceof ConcreteType ? paramName : paramName + ".getValue()";
156 * Template method which generates JAVA comments. InterfaceTemplate
158 * @param comment comment string with the comment for whole JAVA class
159 * @return string with comment in JAVA format
161 public static String asJavadoc(final String comment) {
162 return comment == null ? "" : wrapToDocumentation(formatToParagraph(comment.trim(), 0));
165 private static String formatDataForJavaDoc(final TypeMember type, final String additionalComment) {
166 final StringBuilder javaDoc = new StringBuilder();
167 if (type.getComment() != null && !type.getComment().isEmpty()) {
168 javaDoc.append(formatToParagraph(type.getComment(), 0))
173 javaDoc.append(additionalComment);
174 return wrapToDocumentation(javaDoc.toString());
178 * Returns related Javadoc
179 * @param methodSignature method signature
180 * @return related Javadoc
182 public static String getJavaDocForInterface(final MethodSignature methodSignature) {
183 if (methodSignature.getReturnType() == Types.VOID) {
186 final StringBuilder javaDoc = new StringBuilder();
187 javaDoc.append("@return ")
188 .append(asCode(methodSignature.getReturnType().getFullyQualifiedName()))
190 .append(asCode(propertyNameFromGetter(methodSignature)))
192 .append(asCode("null"))
193 .append(" if not present");
194 return formatDataForJavaDoc(methodSignature, javaDoc.toString());
197 private static String asCode(final String text) {
198 return "<code>" + text + "</code>";
202 * Encodes angle brackets in yang statement description
203 * @param description description of a yang statement which is used to generate javadoc comments
204 * @return string with encoded angle brackets
206 public static String encodeAngleBrackets(String description) {
207 if (description != null) {
208 description = LT_MATCHER.replaceFrom(description, "<");
209 description = GT_MATCHER.replaceFrom(description, ">");
215 * Returns collection of properties as formatted String
216 * @param properties list of given properties
217 * @return generated properties as formatted String
219 public static String propsAsArgs(final Iterable<GeneratedProperty> properties) {
220 return String.join(",", Iterables.transform(properties, prop -> "\"" + prop.getName() + "\""));
224 * Returns properties as formatted String
225 * @param properties list of given properties
226 * @param booleanName Java Boolean type name
227 * @return Properties as formatted String
229 public static String propsAsList(final Iterable<GeneratedProperty> properties, final String booleanName) {
230 return String.join(",", Iterables.transform(properties,
231 prop -> "properties.get(i++).equals(defaultValue) ? " + booleanName + ".TRUE : null"));
235 * Extracts available restrictions from given type
236 * @param currentType given type
237 * @return restrictions from given type
239 public static Restrictions getRestrictions(final Type currentType) {
240 Restrictions restrictions = null;
241 if (currentType instanceof ConcreteType) {
242 restrictions = ((ConcreteType) currentType).getRestrictions();
243 } else if (currentType instanceof GeneratedTransferObject) {
244 restrictions = ((GeneratedTransferObject) currentType).getRestrictions();
250 * sets fieldname according to property for return type
251 * method(type fieldname)
253 * @param property type from getter
254 * @return underscored string form
256 public static String fieldName(final GeneratedProperty property) {
257 final String name = Preconditions.checkNotNull(property.getName());
258 return UNDERSCORE.concat(name);
262 * Template method which generates sequence of the names of the class attributes.
264 * @param parameters group of generated property instances which are transformed to the sequence of parameter names
265 * @return string with the list of the parameter names
267 public static String asArguments(final List<GeneratedProperty> parameters) {
268 return String.join(", ", Lists.transform(parameters, TextTemplateUtil::fieldName));
272 * Helper method for building getter.
274 * @param field property name
275 * @return getter for property
277 public static String getterMethodName(final GeneratedProperty field) {
278 final Type type = Preconditions.checkNotNull(field.getReturnType());
279 final String name = Preconditions.checkNotNull(field.getName());
280 final String prefix = Types.BOOLEAN.equals(type) ? "is" : "get";
281 return prefix.concat(toFirstUpper(name));
285 * Returns built setter method body from input parameters.
286 * @param field generated property
287 * @param typeName type name
288 * @param returnTypeName return type name
289 * @return built setter method body
291 public static String setterMethod(final GeneratedProperty field, final String typeName, final String returnTypeName) {
292 final StringBuilder stringBuilder = new StringBuilder();
293 stringBuilder.append("public ")
296 .append(toFirstUpper(field.getName()))
298 .append(returnTypeName)
299 .append(" value) {\n this.")
300 .append(fieldName(field))
301 .append(" = value;\n return this;\n}\n");
302 return stringBuilder.toString();
306 * Returns simple name of underlying class
307 * @return Simple name of underlying class
309 public static String getSimpleNameForBuilder() {
310 return Builder.class.getSimpleName();
314 * Makes start of getter name uppercase
316 * @param s getter name without prefix
317 * @return getter name starting in uppercase
319 public static String toFirstUpper(final String s) {
320 return s != null && s.length() != 0 ? Character.isUpperCase(s.charAt(0)) ? s : s.length() == 1 ?
321 s.toUpperCase() : s.substring(0, 1).toUpperCase() + s.substring(1) : s;
325 * Cuts prefix from getter name.
327 * @param getter getter name
328 * @return getter name without prefix
330 public static String propertyNameFromGetter(final MethodSignature getter) {
331 final String name = Preconditions.checkNotNull(getter.getName());
333 if (name.startsWith("is")) {
335 } else if (name.startsWith("get")) {
340 return toFirstLower(name.substring(prefix));
344 * Returns list of properties as formatted String.
345 * @param properties input list of generated properties
346 * @return formatted property list as String
348 public static String getPropertyList(final List<GeneratedProperty> properties) {
349 return String.join(", ", Lists.transform(properties, prop -> "base." + getterMethodName(prop) + "()"));
353 * util method for unionTemplateBuilderTemplate
354 * @return string with clarification for javadoc
356 public static String getClarification() {
357 final StringBuilder clarification = new StringBuilder();
358 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")
359 .append("In some cases it is very difficult to automate it since there can be unions such as (uint32 - uint16), or (string - uint32).\n")
361 .append("The reason behind putting it under src/main/java is:\n")
362 .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")
363 .append("loss of user code.\n")
365 return clarification.toString();
369 * Returns source path as String.
370 * @param module module
371 * @param moduleFilePathResolver function module to module file path
372 * @return formatted String source path
374 public static String getSourcePath(final Module module, final Function<Module, Optional<String>> moduleFilePathResolver) {
375 final java.util.Optional<String> moduleFilePath = moduleFilePathResolver.apply(module);
376 Preconditions.checkArgument(moduleFilePath.isPresent(),"Module file path for %s is not present", module);
378 return moduleFilePath.get();
382 * Util method for unionTemplateBuilderTemplate.
383 * @param modifier enum representing Java access modifier
384 * @return needed access modifier
386 public static String getAccessModifier(final AccessModifier modifier) {
388 case PUBLIC: return "public ";
389 case PROTECTED: return "protected ";
390 case PRIVATE: return "private ";
396 * @param text Content of tag description
397 * @param nextLineIndent Number of spaces from left side default is 12
398 * @return formatted description
400 private static String formatToParagraph(final String text, final int nextLineIndent) {
401 if (Strings.isNullOrEmpty(text)) {
404 boolean isFirstElementOnNewLineEmptyChar = false;
405 final StringBuilder sb = new StringBuilder();
406 final StringBuilder lineBuilder = new StringBuilder();
407 final String lineIndent = Strings.repeat(" ", nextLineIndent);
408 final String textToFormat = NEWLINE_OR_TAB.removeFrom(encodeJavadocSymbols(text));
409 final String formattedText = MULTIPLE_SPACES_PATTERN.matcher(textToFormat).replaceAll(" ");
410 final StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true);
412 while (tokenizer.hasMoreElements()) {
413 final String nextElement = tokenizer.nextElement().toString();
415 if (lineBuilder.length() + nextElement.length() > 80) {
416 // Trim trailing whitespace
417 for (int i = lineBuilder.length() - 1; i >= 0 && lineBuilder.charAt(i) != ' '; --i) {
418 lineBuilder.setLength(i);
420 // Trim leading whitespace
421 while (lineBuilder.charAt(0) == ' ') {
422 lineBuilder.deleteCharAt(0);
424 sb.append(lineBuilder).append('\n');
425 lineBuilder.setLength(0);
427 if (nextLineIndent > 0) {
428 sb.append(lineIndent);
431 if (" ".equals(nextElement)) {
432 isFirstElementOnNewLineEmptyChar = true;
435 if (isFirstElementOnNewLineEmptyChar) {
436 isFirstElementOnNewLineEmptyChar = false;
438 lineBuilder.append(nextElement);
441 return sb.append(lineBuilder).append('\n').toString();
444 private static String encodeJavadocSymbols(final String description) {
445 return Strings.isNullOrEmpty(description) ? description
446 : AMP_MATCHER.replaceFrom(TAIL_COMMENT_PATTERN.matcher(description).replaceAll("*/"), "&");