1 package org.opendaylight.yangtools.binding.generator.util;
3 import java.io.ByteArrayOutputStream;
4 import java.io.DataOutputStream;
5 import java.io.IOException;
6 import java.security.MessageDigest;
7 import java.security.NoSuchAlgorithmException;
8 import java.text.DateFormat;
9 import java.text.SimpleDateFormat;
10 import java.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.Collections;
13 import java.util.Comparator;
14 import java.util.HashSet;
15 import java.util.List;
18 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl;
19 import org.opendaylight.yangtools.sal.binding.model.api.AccessModifier;
20 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions;
21 import org.opendaylight.yangtools.sal.binding.model.api.Type;
22 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedPropertyBuilder;
23 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.MethodSignatureBuilder;
24 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.TypeMemberBuilder;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.model.api.Module;
27 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
28 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
29 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
30 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
31 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
32 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
33 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
34 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
37 * Contains the methods for converting strings to valid JAVA language strings
38 * (package names, class names, attribute names).
42 public final class BindingGeneratorUtil {
44 private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyMMdd");
47 * Array of strings values which represents JAVA reserved words.
49 private static final String[] SET_VALUES = new String[] { "abstract", "assert", "boolean", "break", "byte", "case",
50 "catch", "char", "class", "const", "continue", "default", "double", "do", "else", "enum", "extends",
51 "false", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int",
52 "interface", "long", "native", "new", "null", "package", "private", "protected", "public", "return",
53 "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient",
54 "true", "try", "void", "volatile", "while" };
57 * Impossible to instantiate this class. All of the methods or attributes
60 private BindingGeneratorUtil() {
64 * Hash set of words which are reserved in JAVA language.
66 private static final Set<String> JAVA_RESERVED_WORDS = new HashSet<String>(Arrays.asList(SET_VALUES));
69 * Converts string <code>packageName</code> to valid JAVA package name.
71 * If some words of package name are digits of JAVA reserved words they are
72 * prefixed with underscore character.
75 * string which contains words separated by point.
76 * @return package name which contains words separated by point.
78 private static String validateJavaPackage(final String packageName) {
79 if (packageName != null) {
80 final String[] packNameParts = packageName.toLowerCase().split("\\.");
81 if (packNameParts != null) {
82 final StringBuilder builder = new StringBuilder();
83 for (int i = 0; i < packNameParts.length; ++i) {
84 final String packNamePart = packNameParts[i];
85 if (Character.isDigit(packNamePart.charAt(0))) {
86 packNameParts[i] = "_" + packNamePart;
87 } else if (JAVA_RESERVED_WORDS.contains(packNamePart)) {
88 packNameParts[i] = "_" + packNamePart;
93 builder.append(packNameParts[i]);
95 return builder.toString();
102 * Converts <code>parameterName</code> to valid JAVA parameter name.
104 * If the <code>parameterName</code> is one of the JAVA reserved words then
105 * it is prefixed with underscore character.
107 * @param parameterName
108 * string with the parameter name
109 * @return string with the admissible parameter name
111 public static String resolveJavaReservedWordEquivalency(final String parameterName) {
112 if (parameterName != null && JAVA_RESERVED_WORDS.contains(parameterName)) {
113 return "_" + parameterName;
115 return parameterName;
119 * Converts module name to valid JAVA package name.
121 * The package name consists of:
123 * <li>prefix - <i>org.opendaylight.yang.gen.v</i></li>
124 * <li>module YANG version - <i>org.opendaylight.yang.gen.v</i></li>
125 * <li>module namespace - invalid characters are replaced with dots</li>
126 * <li>revision prefix - <i>.rev</i></li>
127 * <li>revision - YYYYMMDD (MM and DD aren't spread to the whole length)</li>
131 * module which contains data about namespace and revision date
132 * @return string with the valid JAVA package name
133 * @throws IllegalArgumentException
134 * if the revision date of the <code>module</code> equals
137 public static String moduleNamespaceToPackageName(final Module module) {
138 final StringBuilder packageNameBuilder = new StringBuilder();
140 if (module.getRevision() == null) {
141 throw new IllegalArgumentException("Module " + module.getName() + " does not specify revision date!");
143 packageNameBuilder.append("org.opendaylight.yang.gen.v");
144 packageNameBuilder.append(module.getYangVersion());
145 packageNameBuilder.append(".");
147 String namespace = module.getNamespace().toString();
148 namespace = namespace.replace("://", ".");
149 namespace = namespace.replace("/", ".");
150 namespace = namespace.replace(":", ".");
151 namespace = namespace.replace("-", ".");
152 namespace = namespace.replace("@", ".");
153 namespace = namespace.replace("$", ".");
154 namespace = namespace.replace("#", ".");
155 namespace = namespace.replace("'", ".");
156 namespace = namespace.replace("*", ".");
157 namespace = namespace.replace("+", ".");
158 namespace = namespace.replace(",", ".");
159 namespace = namespace.replace(";", ".");
160 namespace = namespace.replace("=", ".");
162 packageNameBuilder.append(namespace);
163 packageNameBuilder.append(".rev");
164 packageNameBuilder.append(DATE_FORMAT.format(module.getRevision()));
166 return validateJavaPackage(packageNameBuilder.toString());
170 * Creates package name from specified <code>basePackageName</code> (package
171 * name for module) and <code>schemaPath</code>.
173 * Resulting package name is concatenation of <code>basePackageName</code>
174 * and all local names of YANG nodes which are parents of some node for
175 * which <code>schemaPath</code> is specified.
177 * @param basePackageName
178 * string with package name of the module
180 * list of names of YANG nodes which are parents of some node +
182 * @return string with valid JAVA package name
184 public static String packageNameForGeneratedType(final String basePackageName, final SchemaPath schemaPath) {
185 if (basePackageName == null) {
186 throw new IllegalArgumentException("Base Package Name cannot be NULL!");
188 if (schemaPath == null) {
189 throw new IllegalArgumentException("Schema Path cannot be NULL!");
192 final StringBuilder builder = new StringBuilder();
193 builder.append(basePackageName);
194 final List<QName> pathToNode = schemaPath.getPath();
195 final int traversalSteps = (pathToNode.size() - 1);
196 for (int i = 0; i < traversalSteps; ++i) {
198 String nodeLocalName = pathToNode.get(i).getLocalName();
200 nodeLocalName = nodeLocalName.replace(":", ".");
201 nodeLocalName = nodeLocalName.replace("-", ".");
202 builder.append(nodeLocalName);
204 return validateJavaPackage(builder.toString());
208 * Generates the package name for type definition from
209 * <code>typeDefinition</code> and <code>basePackageName</code>.
211 * @param basePackageName
212 * string with the package name of the module
213 * @param typeDefinition
214 * type definition for which the package name will be generated *
215 * @return string with valid JAVA package name
216 * @throws IllegalArgumentException
218 * <li>if <code>basePackageName</code> equals <code>null</code></li>
219 * <li>if <code>typeDefinition</code> equals <code>null</code></li>
222 public static String packageNameForTypeDefinition(final String basePackageName,
223 final TypeDefinition<?> typeDefinition) {
224 if (basePackageName == null) {
225 throw new IllegalArgumentException("Base Package Name cannot be NULL!");
227 if (typeDefinition == null) {
228 throw new IllegalArgumentException("Type Definition reference cannot be NULL!");
231 final StringBuilder builder = new StringBuilder();
232 builder.append(basePackageName);
233 return validateJavaPackage(builder.toString());
237 * Converts <code>token</code> to string which is in accordance with best
238 * practices for JAVA class names.
241 * string which contains characters which should be converted to
243 * @return string which is in accordance with best practices for JAVA class
246 public static String parseToClassName(String token) {
247 return parseToCamelCase(token, true);
251 * Converts <code>token</code> to string which is in accordance with best
252 * practices for JAVA parameter names.
255 * string which contains characters which should be converted to
256 * JAVA parameter name
257 * @return string which is in accordance with best practices for JAVA
260 public static String parseToValidParamName(final String token) {
261 return resolveJavaReservedWordEquivalency(parseToCamelCase(token, false));
266 * Converts string <code>token</code> to the cammel case format.
269 * string which should be converted to the cammel case format
271 * boolean value which says whether the first character of the
272 * <code>token</code> should|shuldn't be uppercased
273 * @return string in the cammel case format
274 * @throws IllegalArgumentException
276 * <li>if <code>token</code> without white spaces is empty</li>
277 * <li>if <code>token</code> equals null</li>
281 private static String parseToCamelCase(final String token, final boolean uppercase) {
283 throw new IllegalArgumentException("Name can not be null");
286 String correctStr = token.trim();
287 correctStr = correctStr.replace(".", "");
289 if (correctStr.isEmpty()) {
290 throw new IllegalArgumentException("Name can not be emty");
293 correctStr = replaceWithCamelCase(correctStr, ' ');
294 correctStr = replaceWithCamelCase(correctStr, '-');
295 correctStr = replaceWithCamelCase(correctStr, '_');
297 String firstChar = correctStr.substring(0, 1);
299 firstChar = firstChar.toUpperCase();
301 firstChar = firstChar.toLowerCase();
304 if (firstChar.matches("[0-9]")) {
305 correctStr = "_" + correctStr;
307 correctStr = firstChar + correctStr.substring(1);
313 * Replaces all the occurances of the <code>removalChar</code> in the
314 * <code>text</code> with empty string and converts following character to
318 * string with source text which should be converted
320 * character which is sought in the <code>text</code>
321 * @return string which doesn't contain <code>removalChar</code> and has
322 * following characters converted to upper case
323 * @throws IllegalArgumentException
324 * if the length of the returning string has length 0
326 private static String replaceWithCamelCase(String text, char removalChar) {
327 StringBuilder sb = new StringBuilder(text);
328 String toBeRemoved = String.valueOf(removalChar);
330 int toBeRemovedPos = sb.indexOf(toBeRemoved);
331 while (toBeRemovedPos != -1) {
332 sb.replace(toBeRemovedPos, toBeRemovedPos + 1, "");
333 // check if 'toBeRemoved' character is not the only character in
335 if (sb.length() == 0) {
336 throw new IllegalArgumentException("The resulting string can not be empty");
338 String replacement = String.valueOf(sb.charAt(toBeRemovedPos)).toUpperCase();
339 sb.setCharAt(toBeRemovedPos, replacement.charAt(0));
340 toBeRemovedPos = sb.indexOf(toBeRemoved);
342 return sb.toString();
345 public static long computeDefaultSUID(GeneratedTOBuilderImpl to) {
347 ByteArrayOutputStream bout = new ByteArrayOutputStream();
348 DataOutputStream dout = new DataOutputStream(bout);
350 dout.writeUTF(to.getName());
351 dout.writeInt(to.isAbstract() ? 3 : 7);
353 List<Type> impl = to.getImplementsTypes();
354 Collections.sort(impl, new Comparator<Type>() {
356 public int compare(Type o1, Type o2) {
357 return o1.getFullyQualifiedName().compareTo(o2.getFullyQualifiedName());
360 for (Type ifc : impl) {
361 dout.writeUTF(ifc.getFullyQualifiedName());
364 Comparator<TypeMemberBuilder<?>> comparator = new Comparator<TypeMemberBuilder<?>>() {
366 public int compare(TypeMemberBuilder<?> o1, TypeMemberBuilder<?> o2) {
367 return o1.getName().compareTo(o2.getName());
371 List<GeneratedPropertyBuilder> props = to.getProperties();
372 Collections.sort(props, comparator);
373 for (GeneratedPropertyBuilder gp : props) {
374 dout.writeUTF(gp.getName());
377 List<MethodSignatureBuilder> methods = to.getMethodDefinitions();
378 Collections.sort(methods, comparator);
379 for (MethodSignatureBuilder m : methods) {
380 if (!(m.getAccessModifier().equals(AccessModifier.PRIVATE))) {
381 dout.writeUTF(m.getName());
382 dout.write(m.getAccessModifier().ordinal());
388 MessageDigest md = MessageDigest.getInstance("SHA");
389 byte[] hashBytes = md.digest(bout.toByteArray());
391 for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
392 hash = (hash << 8) | (hashBytes[i] & 0xFF);
395 } catch (IOException ex) {
396 throw new InternalError();
397 } catch (NoSuchAlgorithmException ex) {
398 throw new SecurityException(ex.getMessage());
402 public static Restrictions getRestrictions(TypeDefinition<?> type) {
403 final List<LengthConstraint> length = new ArrayList<>();
404 final List<PatternConstraint> pattern = new ArrayList<>();
405 final List<RangeConstraint> range = new ArrayList<>();
407 if (type instanceof ExtendedType) {
408 ExtendedType ext = (ExtendedType)type;
409 TypeDefinition<?> base = ext.getBaseType();
410 length.addAll(ext.getLengthConstraints());
411 pattern.addAll(ext.getPatternConstraints());
412 range.addAll(ext.getRangeConstraints());
414 if (base instanceof IntegerTypeDefinition && range.isEmpty()) {
415 range.addAll(((IntegerTypeDefinition)base).getRangeConstraints());
416 } else if (base instanceof UnsignedIntegerTypeDefinition && range.isEmpty()) {
417 range.addAll(((UnsignedIntegerTypeDefinition)base).getRangeConstraints());
422 return new Restrictions() {
424 public List<RangeConstraint> getRangeConstraints() {
428 public List<PatternConstraint> getPatternConstraints() {
432 public List<LengthConstraint> getLengthConstraints() {
436 public boolean isEmpty() {
437 return range.isEmpty() && pattern.isEmpty() && length.isEmpty();