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 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;
33 public final class TextTemplateUtil {
35 public static final String DOT = ".";
36 public static final String PATTERN_CONSTANT_NAME = "PATTERN_CONSTANTS";
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);
48 private TextTemplateUtil() {
49 throw new UnsupportedOperationException("Util class");
53 * Makes start of getter name LowerCase
55 * @param s getter name without prefix
56 * @return getter name starting in LowerCase
58 public static String toFirstLower(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;
64 * Wraps text as documentation, used in enum description
66 * @param text text for wrapping
67 * @return wrapped text
69 public static String wrapToDocumentation(String text) {
73 final StringBuilder sb = new StringBuilder("/**");
75 Iterable<String> lineSplitText = NL_SPLITTER.split(text);
76 for (final String t : lineSplitText) {
89 * Returns formatted Javadoc, based on type
91 * @return formatted Javadoc, based on type
93 public static String formatDataForJavaDocBuilder(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(GeneratedType type, 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(List<GeneratedProperty> properties) {
129 final List<String> strings = new LinkedList<>();
130 for (GeneratedProperty property : properties) {
131 strings.add(fieldName(property));
133 return String.join(",", strings);
137 * Returns formatted type description
139 * @return formatted type description
141 public static String formatDataForJavaDoc(GeneratedType type) {
142 final String description = type.getDescription().isPresent() ? type.getDescription().get() : "";
143 return encodeJavadocSymbols(description);
147 * Returns parameter name, based on given Type
150 * @return parameter name, based on given Type
152 public static String paramValue(Type returnType, String paramName) {
153 if (returnType instanceof ConcreteType) {
156 return paramName + ".getValue()";
161 * Template method which generates JAVA comments. InterfaceTemplate
163 * @param comment comment string with the comment for whole JAVA class
164 * @return string with comment in JAVA format
166 public static String asJavadoc(String comment) {
167 if (comment == null) {
170 return wrapToDocumentation(formatToParagraph(comment.trim(), 0));
173 private static String formatDataForJavaDoc(TypeMember type, String additionalComment) {
174 final StringBuilder javaDoc = new StringBuilder();
175 if (type.getComment() != null || !type.getComment().isEmpty()) {
176 javaDoc.append(formatToParagraph(type.getComment(), 0))
181 javaDoc.append(additionalComment);
182 return wrapToDocumentation(javaDoc.toString());
186 * Returns related Javadoc
187 * @param methodSignature
188 * @return related Javadoc
190 public static String getJavaDocForInterface(MethodSignature methodSignature) {
191 final StringBuilder javaDoc = new StringBuilder();
192 javaDoc.append("@return ")
193 .append(asCode(methodSignature.getReturnType().getFullyQualifiedName()))
195 .append(asCode(propertyNameFromGetter(methodSignature)))
197 .append(asCode("null"))
198 .append(" if not present");
199 return formatDataForJavaDoc(methodSignature, javaDoc.toString());
202 private static String asCode(String text) {
203 return "<code>" + text + "</code>";
207 * Encodes angle brackets in yang statement description
208 * @param description description of a yang statement which is used to generate javadoc comments
209 * @return string with encoded angle brackets
211 public static String encodeAngleBrackets(String description) {
212 if (description != null) {
213 description = LT_MATCHER.replaceFrom(description, "<");
214 description = GT_MATCHER.replaceFrom(description, ">");
220 * Returns collection of properties as formatted String
222 * @return generated properties as formatted String
224 public static String propsAsArgs(Iterable<GeneratedProperty> properties) {
225 final List<String> strings = new LinkedList<>();
226 if (properties.iterator().hasNext()) {
227 for (GeneratedProperty property : properties) {
228 final StringBuilder stringBuilder = new StringBuilder();
229 stringBuilder.append("\"")
230 .append(property.getName())
232 strings.add(stringBuilder.toString());
235 return String.join(",", strings);
239 * Returns properties as formatted String
242 * @return Properties as formatted String
244 public static String propsAsList(Iterable<GeneratedProperty> properties, String booleanName) {
245 final List<String> strings = new LinkedList<>();
246 if (properties.iterator().hasNext()) {
247 for (GeneratedProperty property : properties) {
248 final StringBuilder stringBuilder = new StringBuilder();
249 stringBuilder.append("properties.get(i++).equals(defaultValue) ? ")
251 .append(".TRUE : null");
252 strings.add(stringBuilder.toString());
255 return String.join(",", strings);
259 * Extracts available restrictions from given type
261 * @return restrictions from given type
263 public static Restrictions getRestrictions(Type currentType) {
264 Restrictions restrictions = null;
265 if (currentType instanceof ConcreteType) {
266 restrictions = ((ConcreteType) currentType).getRestrictions();
267 } else if (currentType instanceof GeneratedTransferObject) {
268 restrictions = ((GeneratedTransferObject) currentType).getRestrictions();
274 * sets fieldname according to property for return type
275 * method(type fieldname)
277 * @param property type from getter
279 public static String fieldName(GeneratedProperty property) {
280 final String name = Preconditions.checkNotNull(property.getName());
281 return UNDERSCORE.concat(name);
285 * Template method which generates sequence of the names of the class attributes
287 * @param parameters group of generated property instances which are transformed to the sequence of parameter names
288 * @return string with the list of the parameter names
290 public static String asArguments(List<GeneratedProperty> parameters) {
291 final List<String> strings = new LinkedList<>();
292 if (!parameters.isEmpty()) {
293 for (GeneratedProperty parameter : parameters) {
294 strings.add((fieldName(parameter)));
297 return String.join(", ", strings);
301 * Helper method for building getter
303 * @param field property name
304 * @return getter for propery
306 public static String getterMethodName(GeneratedProperty field) {
307 final Type type = Preconditions.checkNotNull(field.getReturnType());
308 final String name = Preconditions.checkNotNull(field.getName());
309 final String prefix = Types.BOOLEAN.equals(type) ? "is" : "get";
310 return prefix.concat(toFirstUpper(name));
314 * Returns built setter method body
317 * @param returnTypeName
318 * @return built setter method body
320 public static String setterMethod(GeneratedProperty field, String typeName, String returnTypeName) {
321 final StringBuilder stringBuilder = new StringBuilder();
322 stringBuilder.append("public ")
325 .append(toFirstUpper(field.getName()))
327 .append(returnTypeName)
328 .append(" value) {\n this.")
329 .append(fieldName(field))
330 .append(" = value;\n return this;\n}\n");
331 return stringBuilder.toString();
335 * Returns simple name of underlying class
336 * @return Simple name of underlying class
338 public static String getSimpleNameForBuilder() {
339 return Builder.class.getSimpleName();
343 * Makes start of getter name uppercase
345 * @param s getter name without prefix
346 * @return getter name starting in uppercase
348 public static String toFirstUpper(String s) {
349 return s != null && s.length() != 0 ? (Character.isUpperCase(s.charAt(0)) ? s : (s.length() == 1 ?
350 s.toUpperCase() : s.substring(0, 1).toUpperCase() + s.substring(1))) : s;
354 * Cuts prefix from getter name
356 * @param getter getter name
357 * @return getter name without prefix
359 public static String propertyNameFromGetter(MethodSignature getter) {
360 final String name = Preconditions.checkNotNull(getter.getName());
362 if (name.startsWith("is")) {
364 } else if (name.startsWith("get")) {
367 throw new IllegalArgumentException("Not a getter");
369 return toFirstLower(name.substring(prefix));
373 * Returns list of properties as formatted String
375 * @return formatted property list as String
377 public static String getPropertyList(List<GeneratedProperty> properties) {
378 final List<String> strings = new LinkedList<>();
379 if (!properties.isEmpty()) {
380 for (GeneratedProperty property : properties) {
381 final StringBuilder stringBuilder = new StringBuilder();
382 stringBuilder.append("base.")
383 .append(getterMethodName(property))
385 strings.add(stringBuilder.toString());
388 return String.join(", ", strings);
392 * util method for unionTemplateBuilderTemplate
393 * @return string with clarification for javadoc
395 public static String getClarification() {
396 final StringBuilder clarification = new StringBuilder();
397 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")
398 .append("In some cases it is very difficult to automate it since there can be unions such as (uint32 - uint16), or (string - uint32).\n")
400 .append("The reason behind putting it under src/main/java is:\n")
401 .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")
402 .append("loss of user code.\n")
404 return clarification.toString();
408 * Returns revision Date as String
410 * @return formatted Revision as String
412 public static String getFormattedRevision(Date revision) {
413 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
414 return simpleDateFormat.format(revision);
418 * Returns source path as String
420 * @return formatted String source path
422 public static String getSourcePath(Module module) {
423 return "/".concat(module.getModuleSourcePath().replace(java.io.File.separatorChar, '/'));
427 * util method for unionTemplateBuilderTemplate
428 * @return needed access modifier
430 public static String getAccessModifier(AccessModifier modifier) {
432 case PUBLIC: return "public ";
433 case PROTECTED: return "protected ";
434 case PRIVATE: return "private ";
440 * @param text Content of tag description
441 * @param nextLineIndent Number of spaces from left side default is 12
442 * @return formatted description
444 private static String formatToParagraph(final String text, final int nextLineIndent) {
445 if (Strings.isNullOrEmpty(text)) {
448 boolean isFirstElementOnNewLineEmptyChar = false;
449 final StringBuilder sb = new StringBuilder();
450 final StringBuilder lineBuilder = new StringBuilder();
451 final String lineIndent = Strings.repeat(" ", nextLineIndent);
452 final String textToFormat = NEWLINE_OR_TAB.removeFrom(text);
453 final String formattedText = textToFormat.replaceAll(" +", " ");
454 final StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true);
456 while (tokenizer.hasMoreElements()) {
457 final String nextElement = tokenizer.nextElement().toString();
459 if (lineBuilder.length() + nextElement.length() > 80) {
460 // Trim trailing whitespace
461 for (int i = lineBuilder.length() - 1; i >= 0 && lineBuilder.charAt(i) != ' '; --i) {
462 lineBuilder.setLength(i);
464 // Trim leading whitespace
465 while (lineBuilder.charAt(0) == ' ') {
466 lineBuilder.deleteCharAt(0);
468 sb.append(lineBuilder).append('\n');
469 lineBuilder.setLength(0);
471 if (nextLineIndent > 0) {
472 sb.append(lineIndent);
475 if (" ".equals(nextElement)) {
476 isFirstElementOnNewLineEmptyChar = true;
479 if (isFirstElementOnNewLineEmptyChar) {
480 isFirstElementOnNewLineEmptyChar = false;
482 lineBuilder.append(nextElement);
485 return sb.append(lineBuilder).append('\n').toString();
488 private static String encodeJavadocSymbols(String description) {
489 if (description == null || description.isEmpty()) {
492 final String ret = description.replace("*/", "*/");
493 return AMP_MATCHER.replaceFrom(ret, "&");