2 * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.javav2.generator.util;
10 import com.google.common.annotations.Beta;
11 import java.util.List;
12 import org.opendaylight.mdsal.binding.javav2.model.api.Enumeration;
13 import org.opendaylight.mdsal.binding.javav2.model.api.Enumeration.Pair;
16 * This util class converts every non-java char in identifier to java char by its unicode name
17 * (<a href="http://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.8">JAVA SE
18 * SPEFICIATIONS - Identifiers</a>). There are special types of mapping non-java chars to original
19 * identifiers according to specific {@linkplain JavaIdentifier java type}:
21 * <li>class, enum, interface</li>
24 * <li>without special separator</li>
25 * <li>the first character of identifier, any other first character of identifier part mapped by
26 * non-Java char name from unicode and char in identifier behind non-java char name are converting
31 * <li>example* - ExampleAsterisk</li>
32 * <li>example*example - ExampleAserisksExample</li>
33 * <li>\example - ReverseSolidusExample</li>
34 * <li>1example - DigitOneExample</li>
35 * <li>example1 - Example1</li>
40 * <li>enum value, constant</li>
43 * <li>used underscore as special separator</li>
44 * <li>converted identifier to upper case</li>
48 * <li>example* - EXAMPLE_ASTERISK</li>
49 * <li>example*example - EXAMPLE_ASTERISK_EXAMPLE</li>
50 * <li>\example - REVERSE_SOLIDUS_EXAMPLE</li>
51 * <li>1example - DIGIT_ONE_EXAMPLE</li>
52 * <li>example1 - EXAMPLE1</li>
57 * <li>method, variable
61 * <li>without special separator</li>
62 * <li>the first character of identifier is converting to lower case</li>
63 * <li>any other first character of identifier part mapped by non-Java char name from unicode and
64 * char in identifier behind non-java char name are converting to upper case
68 * <li>example* - exampleAsterisk</li>
69 * <li>example*example - exampleAserisksExample</li>
70 * <li>\example - reverseSolidusExample</li>
71 * <li>1example - digitOneExample</li>
72 * <li>example1 - example1</li>
81 public final class NonJavaCharsConverter {
83 private final static int FIRST_CHAR = 0;
84 private final static int FIRST_INDEX = 1;
86 private NonJavaCharsConverter() {
87 throw new UnsupportedOperationException("Util class");
92 * According to <a href="https://tools.ietf.org/html/rfc7950#section-9.6.4">YANG RFC 7950</a>,
93 * all assigned names in an enumeration MUST be unique. Created names are contained in the list
94 * of {@link Enumeration.Pair}. This method adds actual index with underscore behind name of new
95 * enum value only if this name already exists in one of the list of {@link Enumeration.Pair}.
96 * Then, the name will be converted to java chars according to {@link JavaIdentifier#ENUM_VALUE}
108 * YANG enum values will be mapped to 'FOO' and 'FOO_1' Java enum values.
111 * - name of new enum value
113 * - list of all actual enum values
114 * @return converted and fixed name of new enum value
116 public static String convertIdentifierEnumValue(final String name, final List<Pair> values) {
117 return convertIdentifierEnumValue(name, name, values, FIRST_INDEX);
120 private static String convertIdentifierEnumValue(final String name, final String origName, final List<Pair> values,
122 String newName = name;
123 for (final Pair pair : values) {
124 if (pair.getName().toLowerCase().equals(name.toLowerCase())
125 || pair.getMappedName().toLowerCase().equals(name.toLowerCase())) {
126 int actualRank = rank;
127 final StringBuilder actualNameBuilder = new StringBuilder(origName).append('_').append(actualRank);
128 newName = convertIdentifierEnumValue(actualNameBuilder.toString(), origName, values,
132 return convertIdentifier(newName, JavaIdentifier.ENUM_VALUE);
136 * Find and convert non Java chars in identifiers of generated transfer objects, initially
137 * derived from corresponding YANG.
139 * <a>http://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.8</a>
142 * - name of identifier
143 * @param javaIdentifier
144 * - java type of identifier
145 * @return - java acceptable identifier
147 public static String convertIdentifier(final String identifier, final JavaIdentifier javaIdentifier) {
148 final StringBuilder sb = new StringBuilder();
150 // check and convert first char in identifier if there is non-java char
151 final char firstChar = identifier.charAt(FIRST_CHAR);
152 if (!Character.isJavaIdentifierStart(firstChar)) {
153 // converting first char of identifier
154 sb.append(convertFirst(firstChar, existNext(identifier, FIRST_CHAR)));
156 sb.append(firstChar);
158 // check and convert other chars in identifier, if there is non-java char
159 for (int i = 1; i < identifier.length(); i++) {
160 final char actualChar = identifier.charAt(i);
161 if (!Character.isJavaIdentifierPart(actualChar)) {
162 // prepare actual string of sb for checking if underscore exist on position of the
164 final String partialConvertedIdentifier = sb.toString();
165 sb.append(convert(actualChar, existNext(identifier, i),
166 partialConvertedIdentifier.charAt(partialConvertedIdentifier.length() - 1)));
168 sb.append(actualChar);
171 // apply camel case in appropriate way
172 return fixCasesByJavaType(sb.toString().replace("__", "_").toLowerCase(), javaIdentifier);
176 * Fix cases of converted identifiers by Java type
179 * - converted identifier
180 * @param javaIdentifier
181 * - java type of identifier
182 * @return converted identifier with right cases according to java type
184 private static String fixCasesByJavaType(final String convertedIdentifier, final JavaIdentifier javaIdentifier) {
185 switch (javaIdentifier) {
189 return capitalize(fixCases(convertedIdentifier));
192 return convertedIdentifier.toUpperCase();
195 return fixCases(convertedIdentifier);
197 throw new IllegalArgumentException("Unknown java type of identifier : " + javaIdentifier.toString());
202 * Delete unnecessary chars in converted identifier and apply camel case in appropriate way.
204 * @param convertedIdentifier
205 * - original converted identifier
206 * @return resolved identifier
208 private static String fixCases(final String convertedIdentifier) {
209 final StringBuilder sb = new StringBuilder();
210 if (convertedIdentifier.contains("_")) {
211 boolean isFirst = true;
212 for (final String part : convertedIdentifier.split("_")) {
217 sb.append(capitalize(part));
221 sb.append(convertedIdentifier);
223 return sb.toString();
227 * Check if there exist next char in identifier behind actual char position
230 * - original identifier
232 * - actual char position
233 * @return true if there is another char, false otherwise
235 private static boolean existNext(final String identifier, final int actual) {
236 return (identifier.length() - 1) < (actual + 1) ? false : true;
240 * Converting first char of identifier. This happen only if this char is
246 * - existing of next char behind actual char
247 * @return converted char
249 private static String convertFirst(final char c, final boolean existNext) {
250 String name = Character.getName(c);
251 name = existNext ? (name + "_") : name;
252 return name.contains(" ") ? name.replaceAll(" ", "_") : name;
256 * Converting any char in java identifier, This happen only if this char is
262 * - existing of next char behind actual char
263 * @param partialLastChar
264 * - last char of partial converted identifier
265 * @return converted char
267 private static String convert(final char c, final boolean existNext, final char partialLastChar) {
268 return partialLastChar == '_' ? convertFirst(c, existNext) : "_" + convertFirst(c, existNext);
272 * Capitalize input string
275 * - string to be capitalized
277 private static String capitalize(final String identifier) {
278 return identifier.substring(FIRST_CHAR, FIRST_CHAR + 1).toUpperCase() + identifier.substring(1);