1 package org.opendaylight.yangtools.binding.generator.util;
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;
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;
22 * Contains the methods for converting strings to valid JAVA language strings
23 * (package names, class names, attribute names).
27 public final class BindingGeneratorUtil {
29 private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyMMdd");
32 * Array of strings values which represents JAVA reserved words.
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" };
42 * Impossible to instantiate this class. All of the methods or attributes
45 private BindingGeneratorUtil() {
49 * Hash set of words which are reserved in JAVA language.
51 private static final Set<String> JAVA_RESERVED_WORDS = new HashSet<String>(Arrays.asList(SET_VALUES));
54 * Converts string <code>packageName</code> to valid JAVA package name.
56 * If some words of package name are digits of JAVA reserved words they are
57 * prefixed with underscore character.
60 * string which contains words separated by point.
61 * @return package name which contains words separated by point.
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;
78 builder.append(packNameParts[i]);
80 return builder.toString();
87 * Converts <code>parameterName</code> to valid JAVA parameter name.
89 * If the <code>parameterName</code> is one of the JAVA reserved words then
90 * it is prefixed with underscore character.
92 * @param parameterName
93 * string with the parameter name
94 * @return string with the admissible parameter name
96 public static String resolveJavaReservedWordEquivalency(final String parameterName) {
97 if (parameterName != null && JAVA_RESERVED_WORDS.contains(parameterName)) {
98 return "_" + parameterName;
100 return parameterName;
104 * Converts module name to valid JAVA package name.
106 * The package name consists of:
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>
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
122 public static String moduleNamespaceToPackageName(final Module module) {
123 final StringBuilder packageNameBuilder = new StringBuilder();
125 if (module.getRevision() == null) {
126 throw new IllegalArgumentException("Module " + module.getName() + " does not specify revision date!");
128 packageNameBuilder.append("org.opendaylight.yang.gen.v");
129 packageNameBuilder.append(module.getYangVersion());
130 packageNameBuilder.append(".");
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("=", ".");
147 packageNameBuilder.append(namespace);
148 packageNameBuilder.append(".rev");
149 packageNameBuilder.append(DATE_FORMAT.format(module.getRevision()));
151 return validateJavaPackage(packageNameBuilder.toString());
155 * Creates package name from specified <code>basePackageName</code> (package
156 * name for module) and <code>schemaPath</code>.
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.
162 * @param basePackageName
163 * string with package name of the module
165 * list of names of YANG nodes which are parents of some node +
167 * @return string with valid JAVA package name
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!");
173 if (schemaPath == null) {
174 throw new IllegalArgumentException("Schema Path cannot be NULL!");
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) {
183 String nodeLocalName = pathToNode.get(i).getLocalName();
185 nodeLocalName = nodeLocalName.replace(":", ".");
186 nodeLocalName = nodeLocalName.replace("-", ".");
187 builder.append(nodeLocalName);
189 return validateJavaPackage(builder.toString());
193 * Generates the package name for type definition from
194 * <code>typeDefinition</code> and <code>basePackageName</code>.
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
203 * <li>if <code>basePackageName</code> equals <code>null</code></li>
204 * <li>if <code>typeDefinition</code> equals <code>null</code></li>
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!");
212 if (typeDefinition == null) {
213 throw new IllegalArgumentException("Type Definition reference cannot be NULL!");
216 final StringBuilder builder = new StringBuilder();
217 builder.append(basePackageName);
218 return validateJavaPackage(builder.toString());
222 * Converts <code>token</code> to string which is in accordance with best
223 * practices for JAVA class names.
226 * string which contains characters which should be converted to
228 * @return string which is in accordance with best practices for JAVA class
231 public static String parseToClassName(String token) {
232 return parseToCamelCase(token, true);
236 * Converts <code>token</code> to string which is in accordance with best
237 * practices for JAVA parameter names.
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
245 public static String parseToValidParamName(final String token) {
246 return resolveJavaReservedWordEquivalency(parseToCamelCase(token, false));
251 * Converts string <code>token</code> to the cammel case format.
254 * string which should be converted to the cammel case format
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
261 * <li>if <code>token</code> without white spaces is empty</li>
262 * <li>if <code>token</code> equals null</li>
266 private static String parseToCamelCase(final String token, final boolean uppercase) {
268 throw new IllegalArgumentException("Name can not be null");
271 String correctStr = token.trim();
272 correctStr = correctStr.replace(".", "");
274 if (correctStr.isEmpty()) {
275 throw new IllegalArgumentException("Name can not be emty");
278 correctStr = replaceWithCamelCase(correctStr, ' ');
279 correctStr = replaceWithCamelCase(correctStr, '-');
280 correctStr = replaceWithCamelCase(correctStr, '_');
282 String firstChar = correctStr.substring(0, 1);
284 firstChar = firstChar.toUpperCase();
286 firstChar = firstChar.toLowerCase();
289 if (firstChar.matches("[0-9]")) {
290 correctStr = "_" + correctStr;
292 correctStr = firstChar + correctStr.substring(1);
298 * Replaces all the occurances of the <code>removalChar</code> in the
299 * <code>text</code> with empty string and converts following character to
303 * string with source text which should be converted
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
311 private static String replaceWithCamelCase(String text, char removalChar) {
312 StringBuilder sb = new StringBuilder(text);
313 String toBeRemoved = String.valueOf(removalChar);
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
320 if (sb.length() == 0) {
321 throw new IllegalArgumentException("The resulting string can not be empty");
323 String replacement = String.valueOf(sb.charAt(toBeRemovedPos)).toUpperCase();
324 sb.setCharAt(toBeRemovedPos, replacement.charAt(0));
325 toBeRemovedPos = sb.indexOf(toBeRemoved);
327 return sb.toString();
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<>();
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());
342 return new Restrictions() {
344 public List<RangeConstraint> getRangeConstraints() {
348 public List<PatternConstraint> getPatternConstraints() {
352 public List<LengthConstraint> getLengthConstraints() {
356 public boolean isEmpty() {
357 return range.isEmpty() && pattern.isEmpty() && length.isEmpty();