Bug 1459-3 #3 - Re-organize mdsal-binding2-generator-impl
[mdsal.git] / binding2 / mdsal-binding2-generator-impl / src / main / java / org / opendaylight / mdsal / binding / javav2 / generator / impl / util / YangTextTemplate.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 package org.opendaylight.mdsal.binding.javav2.generator.impl.util;
9
10 import com.google.common.annotations.Beta;
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 java.util.Map;
17 import java.util.StringTokenizer;
18 import org.opendaylight.mdsal.binding2.generator.util.Types;
19 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
20 import org.opendaylight.mdsal.binding.javav2.model.api.ParameterizedType;
21 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
22 import org.opendaylight.mdsal.binding.javav2.model.api.WildcardType;
23 import org.opendaylight.yangtools.yang.common.QName;
24
25 /**
26  * Util class
27  */
28 @Beta
29 public final class YangTextTemplate {
30     private static final CharMatcher NEWLINE_OR_TAB = CharMatcher.anyOf("\n\t");
31     private static final String DOT = ".";
32     private static final String COMMA = ",";
33     private static final char NEW_LINE = '\n';
34     private static final CharMatcher NL_MATCHER = CharMatcher.is(NEW_LINE);
35     private static final CharMatcher AMP_MATCHER = CharMatcher.is('&');
36     private static final Splitter NL_SPLITTER = Splitter.on(NL_MATCHER);
37
38     private YangTextTemplate() {
39         throw new UnsupportedOperationException("Util class");
40     }
41
42     public static String formatSchemaPath(final String moduleName, final Iterable<QName> schemaPath) {
43         final StringBuilder sb = new StringBuilder();
44         sb.append(moduleName);
45
46         QName currentElement = Iterables.getFirst(schemaPath, null);
47         for (QName pathElement : schemaPath) {
48             sb.append('/');
49             if (!currentElement.getNamespace().equals(pathElement.getNamespace())) {
50                 currentElement = pathElement;
51                 sb.append(pathElement);
52             } else {
53                 sb.append(pathElement.getLocalName());
54             }
55         }
56         return sb.toString();
57     }
58
59     /**
60      * Used in #yangtemplateformodule.scala.txt for formating revision description
61      *
62      * @param text Content of tag description
63      * @param nextLineIndent Number of spaces from left side default is 12
64      * @return formatted description
65      */
66     public static String formatToParagraph(final String text, final int nextLineIndent) {
67         if (Strings.isNullOrEmpty(text)) {
68             return "";
69         }
70         boolean isFirstElementOnNewLineEmptyChar = false;
71         final StringBuilder sb = new StringBuilder();
72         final StringBuilder lineBuilder = new StringBuilder();
73         final String lineIndent = Strings.repeat(" ", nextLineIndent);
74         final String textToFormat = NEWLINE_OR_TAB.removeFrom(text);
75         final String formattedText = textToFormat.replaceAll(" +", " ");
76         final StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true);
77
78         while (tokenizer.hasMoreElements()) {
79             final String nextElement = tokenizer.nextElement().toString();
80
81             if (lineBuilder.length() + nextElement.length() > 80) {
82                 // Trim trailing whitespace
83                 for (int i = lineBuilder.length() - 1; i >= 0 && lineBuilder.charAt(i) != ' '; --i) {
84                     lineBuilder.setLength(i);
85                 }
86                 // Trim leading whitespace
87                 while (lineBuilder.charAt(0) == ' ') {
88                     lineBuilder.deleteCharAt(0);
89                 }
90                 sb.append(lineBuilder).append('\n');
91                 lineBuilder.setLength(0);
92
93                 if (nextLineIndent > 0) {
94                     sb.append(lineIndent);
95                 }
96
97                 if (" ".equals(nextElement)) {
98                     isFirstElementOnNewLineEmptyChar = true;
99                 }
100             }
101             if (isFirstElementOnNewLineEmptyChar) {
102                 isFirstElementOnNewLineEmptyChar = false;
103             } else {
104                 lineBuilder.append(nextElement);
105             }
106         }
107         return sb.append(lineBuilder).append('\n').toString();
108     }
109
110     /**
111      * Used in all yangtemplates for formating augmentation target
112      *
113      * @param schemaPath path to augmented node
114      * @return path in string format
115      */
116     public static String formatToAugmentPath(final Iterable<QName> schemaPath) {
117         final StringBuilder sb = new StringBuilder();
118         for (QName pathElement : schemaPath) {
119             sb.append("\\(")
120             .append(pathElement.getNamespace())
121             .append(')')
122             .append(pathElement.getLocalName());
123         }
124         return sb.toString();
125     }
126
127     /**
128      * Evaluates if it is necessary to add the package name for type to the map of imports for parentGenType
129      * If it is so the package name is saved to the map imports.
130      *
131      * @param parentGenType generated type for which is the map of necessary imports build
132      * @param type JAVA type for which is the necessary of the package import evaluated
133      * @param imports map of the imports for parentGenType
134      */
135     public static void putTypeIntoImports(final GeneratedType parentGenType, final Type type,
136         final Map<String, String> imports) {
137         Preconditions.checkArgument(parentGenType != null,
138                 "Parent Generated Type parameter MUST be specified and cannot be NULL!");
139         Preconditions.checkArgument(type != null, "Type parameter MUST be specified and cannot be NULL!");
140         Preconditions.checkArgument(parentGenType.getPackageName() != null,
141                 "Parent Generated Type cannot have Package Name referenced as NULL!");
142
143         final String typeName = Preconditions.checkNotNull(type.getName());
144         final String typePackageName = Preconditions.checkNotNull(type.getPackageName());
145         final String parentTypeName = Preconditions.checkNotNull(parentGenType.getName());
146         if (typeName.equals(parentTypeName) || typePackageName.startsWith("java.lang") || typePackageName.isEmpty()) {
147             return;
148         }
149         if (!imports.containsKey(typeName)) {
150             imports.put(typeName, typePackageName);
151         }
152         if (type instanceof ParameterizedType) {
153             final ParameterizedType paramType = (ParameterizedType) type;
154             final Type[] params = paramType.getActualTypeArguments();
155             if (params != null) {
156                 for (Type param : params) {
157                     putTypeIntoImports(parentGenType, param, imports);
158                 }
159             }
160         }
161     }
162
163     /**
164      * Builds the string which contains either the full path to the type (package name with type) or only type name
165      * if the package is among imports.
166      *
167      * @param parentGenType generated type which contains type
168      * @param type JAVA type for which is the string with type info generated
169      * @param imports map of necessary imports for parentGenType
170      * @return string with type name for type in the full format or in the short format
171      */
172     public static String getExplicitType(final GeneratedType parentGenType, final Type type,
173         final Map<String, String> imports) {
174         Preconditions.checkArgument(type != null, "Type parameter MUST be specified and cannot be NULL!");
175         Preconditions.checkArgument(imports != null, "Imports Map cannot be NULL!");
176
177         final String typePackageName = Preconditions.checkNotNull(type.getPackageName());
178         final String typeName = Preconditions.checkNotNull(type.getName());
179         final String importedPackageName = imports.get(typeName);
180         final StringBuilder builder;
181         if (typePackageName.equals(importedPackageName)) {
182             builder = new StringBuilder(typeName);
183             addActualTypeParameters(builder, type, parentGenType, imports);
184             if ("Void".equals(builder.toString())) {
185                 return "void";
186             }
187         } else {
188             builder = new StringBuilder();
189             if (!typePackageName.isEmpty()) {
190                 builder.append(typePackageName + DOT + typeName);
191             } else {
192                 builder.append(type.getName());
193             }
194             if (type.equals(Types.voidType())) {
195                 return "void";
196             }
197             addActualTypeParameters(builder, type, parentGenType, imports);
198         }
199         return builder.toString();
200     }
201
202     /**
203      * Adds actual type parameters from type to builder if type is ParametrizedType.
204      *
205      * @param builder string builder which contains type name
206      * @param type JAVA Type for which is the string with type info generated
207      * @param parentGenType generated type which contains type
208      * @param imports map of necessary imports for parentGenType
209      * @return adds actual type parameters to builder
210      */
211     private static StringBuilder addActualTypeParameters(final StringBuilder builder, final Type type,
212         final GeneratedType parentGenType, final Map<String, String> imports) {
213         if (type instanceof ParameterizedType) {
214             final ParameterizedType pType = (ParameterizedType) type;
215             final Type[] pTypes = pType.getActualTypeArguments();
216             builder.append("<");
217             builder.append(getParameters(parentGenType, pTypes, imports));
218             builder.append(">");
219         }
220         return builder;
221     }
222
223     /**
224      * Generates the string with all actual type parameters from
225      *
226      * @param parentGenType generated type for which is the JAVA code generated
227      * @param pTypes array of Type instances = actual type parameters
228      * @param availableImports map of imports for parentGenType
229      * @return string with all actual type parameters from pTypes
230      */
231     private static String getParameters(final GeneratedType parentGenType, final Type[] pTypes,
232         final Map<String, String> availableImports) {
233         if (pTypes == null || pTypes.length == 0) {
234             return "?";
235         }
236         final StringBuilder builder = new StringBuilder();
237         for (int i = 0; i < pTypes.length; i++) {
238             final Type t = pTypes[i];
239
240             String separator = COMMA;
241             if (i == (pTypes.length - 1)) {
242                 separator = "";
243             }
244
245             String wildcardParam = "";
246             if (t.equals(Types.voidType())) {
247                 builder.append("java.lang.Void")
248                 .append(separator);
249                 continue;
250             } else {
251
252                 if (t instanceof WildcardType) {
253                     wildcardParam = "? extends ";
254                 }
255
256                 builder.append(wildcardParam)
257                 .append(getExplicitType(parentGenType, t, availableImports) + separator);
258             }
259         }
260         return builder.toString();
261     }
262
263     /**
264      * Wraps text as documentation
265      *
266      * @param text text for wrapping
267      * @return wrapped text
268      */
269     public static String wrapToDocumentation(String text) {
270         if (text.isEmpty()) {
271             return "";
272         }
273         final StringBuilder sb = new StringBuilder("/**");
274         sb.append(NEW_LINE);
275         Iterable<String> lineSplitText = NL_SPLITTER.split(text);
276         for (final String t : lineSplitText) {
277             sb.append(" *");
278             if (t.isEmpty()) {
279                 sb.append(" ");
280                 sb.append(t);
281             }
282             sb.append(NEW_LINE);
283         }
284         sb.append(" */");
285         return sb.toString();
286     }
287
288     public static String encodeJavadocSymbols(String description) {
289         if (description == null || description.isEmpty()) {
290             return description;
291         }
292         String ret = description.replace("*/", "&#42;&#47;");
293         return AMP_MATCHER.replaceFrom(ret, "&amp;");
294     }
295 }