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