Tests for class BindingGeneratorUtil
[yangtools.git] / code-generator / binding-generator-util / src / main / java / org / opendaylight / yangtools / binding / generator / util / BindingGeneratorUtil.java
1 package org.opendaylight.yangtools.binding.generator.util;
2
3 import java.text.DateFormat;
4 import java.text.SimpleDateFormat;
5 import java.util.Arrays;
6 import java.util.HashSet;
7 import java.util.List;
8 import java.util.Set;
9
10 import org.opendaylight.yangtools.yang.common.QName;
11 import org.opendaylight.yangtools.yang.model.api.Module;
12 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
13 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
14
15 /**
16  * Contains the methods for converting strings to valid JAVA language strings
17  * (package names, class names, attribute names).
18  * 
19  * 
20  */
21 public final class BindingGeneratorUtil {
22
23     private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyMMdd");
24
25     /**
26      * Array of strings values which represents JAVA reserved words.
27      */
28     private static final String[] SET_VALUES = new String[] { "abstract", "assert", "boolean", "break", "byte", "case",
29             "catch", "char", "class", "const", "continue", "default", "double", "do", "else", "enum", "extends",
30             "false", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int",
31             "interface", "long", "native", "new", "null", "package", "private", "protected", "public", "return",
32             "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient",
33             "true", "try", "void", "volatile", "while" };
34
35     /**
36      * Impossible to instantiate this class. All of the methods or attributes
37      * are static.
38      */
39     private BindingGeneratorUtil() {
40     }
41
42     /**
43      * Hash set of words which are reserved in JAVA language.
44      */
45     private static final Set<String> JAVA_RESERVED_WORDS = new HashSet<String>(Arrays.asList(SET_VALUES));
46
47     /**
48      * Converts string <code>packageName</code> to valid JAVA package name.
49      * 
50      * If some words of package name are digits of JAVA reserved words they are
51      * prefixed with underscore character.
52      * 
53      * @param packageName
54      *            string which contains words separated by point.
55      * @return package name which contains words separated by point.
56      */
57     private static String validateJavaPackage(final String packageName) {
58         if (packageName != null) {
59             final String[] packNameParts = packageName.split("\\.");
60             if (packNameParts != null) {
61                 final StringBuilder builder = new StringBuilder();
62                 for (int i = 0; i < packNameParts.length; ++i) {
63                     final String packNamePart = packNameParts[i];
64                     if (Character.isDigit(packNamePart.charAt(0))) {
65                         packNameParts[i] = "_" + packNamePart;
66                     } else if (JAVA_RESERVED_WORDS.contains(packNamePart)) {
67                         packNameParts[i] = "_" + packNamePart;
68                     }
69                     if (i > 0) {
70                         builder.append(".");
71                     }
72                     builder.append(packNameParts[i]);
73                 }
74                 return builder.toString();
75             }
76         }
77         return packageName;
78     }
79
80     /**
81      * Converts <code>parameterName</code> to valid JAVA parameter name.
82      * 
83      * If the <code>parameterName</code> is one of the JAVA reserved words then
84      * it is prefixed with underscore character.
85      * 
86      * @param parameterName
87      *            string with the parameter name
88      * @return string with the admissible parameter name
89      */
90     public static String resolveJavaReservedWordEquivalency(final String parameterName) {
91         if (parameterName != null && JAVA_RESERVED_WORDS.contains(parameterName)) {
92             return "_" + parameterName;
93         }
94         return parameterName;
95     }
96
97     /**
98      * Converts module name to valid JAVA package name.
99      * 
100      * The package name consists of:
101      * <ul>
102      * <li>prefix - <i>org.opendaylight.yang.gen.v</i></li>
103      * <li>module YANG version - <i>org.opendaylight.yang.gen.v</i></li>
104      * <li>module namespace - invalid characters are replaced with dots</li>
105      * <li>revision prefix - <i>.rev</i></li>
106      * <li>revision - YYYYMMDD (MM and DD aren't spread to the whole length)</li>
107      * </ul>
108      * 
109      * @param module
110      *            module which contains data about namespace and revision date
111      * @return string with the valid JAVA package name
112      * @throws IllegalArgumentException
113      *             if the revision date of the <code>module</code> equals
114      *             <code>null</code>
115      */
116     public static String moduleNamespaceToPackageName(final Module module) {
117         final StringBuilder packageNameBuilder = new StringBuilder();
118
119         if (module.getRevision() == null) {
120             throw new IllegalArgumentException("Module " + module.getName() + " does not specify revision date!");
121         }
122         packageNameBuilder.append("org.opendaylight.yang.gen.v");
123         packageNameBuilder.append(module.getYangVersion());
124         packageNameBuilder.append(".");
125
126         String namespace = module.getNamespace().toString();
127         namespace = namespace.replace("://", ".");
128         namespace = namespace.replace("/", ".");
129         namespace = namespace.replace(":", ".");
130         namespace = namespace.replace("-", ".");
131         namespace = namespace.replace("@", ".");
132         namespace = namespace.replace("$", ".");
133         namespace = namespace.replace("#", ".");
134         namespace = namespace.replace("'", ".");
135         namespace = namespace.replace("*", ".");
136         namespace = namespace.replace("+", ".");
137         namespace = namespace.replace(",", ".");
138         namespace = namespace.replace(";", ".");
139         namespace = namespace.replace("=", ".");
140
141         packageNameBuilder.append(namespace);
142         packageNameBuilder.append(".rev");
143         packageNameBuilder.append(DATE_FORMAT.format(module.getRevision()));
144
145         return validateJavaPackage(packageNameBuilder.toString());
146     }
147
148     /**
149      * Creates package name from specified <code>basePackageName</code> (package
150      * name for module) and <code>schemaPath</code>.
151      * 
152      * Resulting package name is concatenation of <code>basePackageName</code>
153      * and all local names of YANG nodes which are parents of some node for
154      * which <code>schemaPath</code> is specified.
155      * 
156      * @param basePackageName
157      *            string with package name of the module
158      * @param schemaPath
159      *            list of names of YANG nodes which are parents of some node +
160      *            name of this node
161      * @return string with valid JAVA package name
162      */
163     public static String packageNameForGeneratedType(final String basePackageName, final SchemaPath schemaPath) {
164         if (basePackageName == null) {
165             throw new IllegalArgumentException("Base Package Name cannot be NULL!");
166         }
167         if (schemaPath == null) {
168             throw new IllegalArgumentException("Schema Path cannot be NULL!");
169         }
170
171         final StringBuilder builder = new StringBuilder();
172         builder.append(basePackageName);
173         final List<QName> pathToNode = schemaPath.getPath();
174         final int traversalSteps = (pathToNode.size() - 1);
175         for (int i = 0; i < traversalSteps; ++i) {
176             builder.append(".");
177             String nodeLocalName = pathToNode.get(i).getLocalName();
178
179             nodeLocalName = nodeLocalName.replace(":", ".");
180             nodeLocalName = nodeLocalName.replace("-", ".");
181             builder.append(nodeLocalName);
182         }
183         return validateJavaPackage(builder.toString());
184     }
185
186     /**
187      * Generates the package name for type definition from
188      * <code>typeDefinition</code> and <code>basePackageName</code>.
189      * 
190      * @param basePackageName
191      *            string with the package name of the module
192      * @param typeDefinition
193      *            type definition for which the package name will be generated *
194      * @return string with valid JAVA package name
195      * @throws IllegalArgumentException
196      *             <ul>
197      *             <li>if <code>basePackageName</code> equals <code>null</code></li>
198      *             <li>if <code>typeDefinition</code> equals <code>null</code></li>
199      *             </ul>
200      */
201     public static String packageNameForTypeDefinition(final String basePackageName,
202             final TypeDefinition<?> typeDefinition) {
203         if (basePackageName == null) {
204             throw new IllegalArgumentException("Base Package Name cannot be NULL!");
205         }
206         if (typeDefinition == null) {
207             throw new IllegalArgumentException("Type Definition reference cannot be NULL!");
208         }
209
210         final StringBuilder builder = new StringBuilder();
211         builder.append(basePackageName);
212         return validateJavaPackage(builder.toString());
213     }
214
215     /**
216      * Converts <code>token</code> to string which is in accordance with best
217      * practices for JAVA class names.
218      * 
219      * @param token
220      *            string which contains characters which should be converted to
221      *            JAVA class name
222      * @return string which is in accordance with best practices for JAVA class
223      *         name.
224      */
225     public static String parseToClassName(String token) {
226         return parseToCamelCase(token, true);
227     }
228
229     /**
230      * Converts <code>token</code> to string which is in accordance with best
231      * practices for JAVA parameter names.
232      * 
233      * @param token
234      *            string which contains characters which should be converted to
235      *            JAVA parameter name
236      * @return string which is in accordance with best practices for JAVA
237      *         parameter name.
238      */
239     public static String parseToValidParamName(final String token) {
240         return resolveJavaReservedWordEquivalency(parseToCamelCase(token, false));
241     }
242
243     /**
244      * 
245      * Converts string <code>token</code> to the cammel case format.
246      * 
247      * @param token
248      *            string which should be converted to the cammel case format
249      * @param uppercase
250      *            boolean value which says whether the first character of the
251      *            <code>token</code> should|shuldn't be uppercased
252      * @return string in the cammel case format
253      * @throws IllegalArgumentException
254      *             <ul>
255      *             <li>if <code>token</code> without white spaces is empty</li>
256      *             <li>if <code>token</code> equals null</li>
257      *             </ul>
258      */
259
260     private static String parseToCamelCase(final String token, final boolean uppercase) {
261         if (token == null) {
262             throw new IllegalArgumentException("Name can not be null");
263         }
264
265         String correctStr = token.trim();
266         correctStr = correctStr.replace(".", "");
267
268         if (correctStr.isEmpty()) {
269             throw new IllegalArgumentException("Name can not be emty");
270         }
271
272         correctStr = replaceWithCamelCase(correctStr, ' ');
273         correctStr = replaceWithCamelCase(correctStr, '-');
274         correctStr = replaceWithCamelCase(correctStr, '_');
275
276         String firstChar = correctStr.substring(0, 1);
277         if (uppercase) {
278             firstChar = firstChar.toUpperCase();
279         } else {
280             firstChar = firstChar.toLowerCase();
281         }
282
283         if (firstChar.matches("[0-9]")) {
284             correctStr = "_" + correctStr;
285         } else {
286             correctStr = firstChar + correctStr.substring(1);
287         }
288         return correctStr;
289     }
290
291     /**
292      * Replaces all the occurances of the <code>removalChar</code> in the
293      * <code>text</code> with empty string and converts following character to
294      * upper case.
295      * 
296      * @param text
297      *            string with source text which should be converted
298      * @param removalChar
299      *            character which is sought in the <code>text</code>
300      * @return string which doesn't contain <code>removalChar</code> and has
301      *         following characters converted to upper case
302      * @throws IllegalArgumentException
303      *             if the length of the returning string has length 0
304      */
305     private static String replaceWithCamelCase(String text, char removalChar) {
306         StringBuilder sb = new StringBuilder(text);
307         String toBeRemoved = String.valueOf(removalChar);
308
309         int toBeRemovedPos = sb.indexOf(toBeRemoved);
310         while (toBeRemovedPos != -1) {
311             sb.replace(toBeRemovedPos, toBeRemovedPos + 1, "");
312             // check if 'toBeRemoved' character is not the only character in
313             // 'text'
314             if (sb.length() == 0) {
315                 throw new IllegalArgumentException("The resulting string can not be empty");
316             }
317             String replacement = String.valueOf(sb.charAt(toBeRemovedPos)).toUpperCase();
318             sb.setCharAt(toBeRemovedPos, replacement.charAt(0));
319             toBeRemovedPos = sb.indexOf(toBeRemoved);
320         }
321         return sb.toString();
322     }
323 }