package org.opendaylight.mdsal.binding.model.util;
import com.google.common.base.CharMatcher;
+import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.Iterables;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
+import java.util.Optional;
+import java.util.regex.Pattern;
import org.opendaylight.mdsal.binding.model.api.AccessModifier;
import org.opendaylight.mdsal.binding.model.api.Restrictions;
import org.opendaylight.mdsal.binding.model.api.Type;
import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuilder;
import org.opendaylight.mdsal.binding.model.api.type.builder.TypeMemberBuilder;
-import org.opendaylight.yangtools.yang.binding.BindingMapping;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.QNameModule;
-import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
import org.opendaylight.yangtools.yang.model.util.type.DecimalTypeBuilder;
public final class BindingGeneratorUtil {
/**
- * Impossible to instantiate this class. All of the methods or attributes
- * are static.
+ * Impossible to instantiate this class. All of the methods or attributes are static.
*/
private BindingGeneratorUtil() {
+
}
/**
* Pre-compiled replacement pattern.
*/
- private static final CharMatcher DOT_MATCHER = CharMatcher.is('.');
private static final CharMatcher DASH_COLON_MATCHER = CharMatcher.anyOf("-:");
private static final CharMatcher GT_MATCHER = CharMatcher.is('>');
private static final CharMatcher LT_MATCHER = CharMatcher.is('<');
+ private static final Pattern UNICODE_CHAR_PATTERN = Pattern.compile("\\\\+u");
private static final Restrictions EMPTY_RESTRICTIONS = new Restrictions() {
@Override
- public List<LengthConstraint> getLengthConstraints() {
- return Collections.emptyList();
+ public Optional<LengthConstraint> getLengthConstraint() {
+ return Optional.empty();
}
@Override
}
@Override
- public List<RangeConstraint> getRangeConstraints() {
- return Collections.emptyList();
+ public Optional<RangeConstraint<?>> getRangeConstraint() {
+ return Optional.empty();
}
@Override
};
private static final Comparator<TypeMemberBuilder<?>> SUID_MEMBER_COMPARATOR =
- (o1, o2) -> o1.getName().compareTo(o2.getName());
+ Comparator.comparing(TypeMemberBuilder::getName);
- private static final Comparator<Type> SUID_NAME_COMPARATOR =
- (o1, o2) -> o1.getFullyQualifiedName().compareTo(o2.getFullyQualifiedName());
+ private static final Comparator<Type> SUID_NAME_COMPARATOR = Comparator.comparing(Type::getFullyQualifiedName);
/**
- * Converts <code>parameterName</code> to valid JAVA parameter name.
- *
- * If the <code>parameterName</code> is one of the JAVA reserved words then
- * it is prefixed with underscore character.
+ * Converts <code>parameterName</code> to valid JAVA parameter name. If the <code>parameterName</code> is one
+ * of the JAVA reserved words then it is prefixed with underscore character.
*
- * @param parameterName
- * string with the parameter name
+ * @param parameterName string with the parameter name
* @return string with the admissible parameter name
*/
public static String resolveJavaReservedWordEquivalency(final String parameterName) {
- if ((parameterName != null) && BindingMapping.JAVA_RESERVED_WORDS.contains(parameterName)) {
+ if (parameterName != null && BindingMapping.JAVA_RESERVED_WORDS.contains(parameterName)) {
return "_" + parameterName;
}
return parameterName;
}
/**
- * Converts module name to valid JAVA package name.
- *
- * The package name consists of:
- * <ul>
- * <li>prefix - <i>org.opendaylight.yang.gen.v</i></li>
- * <li>module YANG version - <i>org.opendaylight.yang.gen.v</i></li>
- * <li>module namespace - invalid characters are replaced with dots</li>
- * <li>revision prefix - <i>.rev</i></li>
- * <li>revision - YYYYMMDD (MM and DD aren't spread to the whole length)</li>
- * </ul>
- *
- * @param module
- * module which contains data about namespace and revision date
- * @return string with the valid JAVA package name
- * @throws IllegalArgumentException
- * if the revision date of the <code>module</code> equals
- * <code>null</code>
- * @deprecated USe {@link BindingMapping#getRootPackageName(QNameModule)} with {@link Module#getQNameModule()}.
- */
- @Deprecated
- public static String moduleNamespaceToPackageName(final Module module) {
- return BindingMapping.getRootPackageName(module.getQNameModule());
- }
-
- /**
- * Creates package name from specified <code>basePackageName</code> (package
- * name for module) and <code>schemaPath</code>.
+ * Creates package name from specified <code>basePackageName</code> (package name for module)
+ * and <code>schemaPath</code>. Resulting package name is concatenation of <code>basePackageName</code>
+ * and all local names of YANG nodes which are parents of some node for which <code>schemaPath</code> is specified.
*
- * Resulting package name is concatenation of <code>basePackageName</code>
- * and all local names of YANG nodes which are parents of some node for
- * which <code>schemaPath</code> is specified.
- *
- * @param basePackageName
- * string with package name of the module, MUST be normalized,
- * otherwise this method may return an invalid string.
- * @param schemaPath
- * list of names of YANG nodes which are parents of some node +
- * name of this node
+ * @param basePackageName string with package name of the module, MUST be normalized, otherwise this method may
+ * return an invalid string.
+ * @param schemaPath list of names of YANG nodes which are parents of some node + name of this node
* @return string with valid JAVA package name
* @throws NullPointerException if any of the arguments are null
*/
}
/**
- * Creates package name from specified <code>basePackageName</code> (package
- * name for module) and <code>schemaPath</code> which crosses an augmentation.
- *
- * Resulting package name is concatenation of <code>basePackageName</code>
- * and all local names of YANG nodes which are parents of some node for
- * which <code>schemaPath</code> is specified.
+ * Creates package name from specified <code>basePackageName</code> (package name for module)
+ * and <code>schemaPath</code> which crosses an augmentation. Resulting package name is concatenation
+ * of <code>basePackageName</code> and all local names of YANG nodes which are parents of some node for which
+ * <code>schemaPath</code> is specified.
*
- * @param basePackageName
- * string with package name of the module, MUST be normalized,
- * otherwise this method may return an invalid string.
- * @param schemaPath
- * list of names of YANG nodes which are parents of some node +
- * name of this node
+ * @param basePackageName string with package name of the module, MUST be normalized, otherwise this method may
+ * return an invalid string.
+ * @param schemaPath list of names of YANG nodes which are parents of some node + name of this node
* @return string with valid JAVA package name
* @throws NullPointerException if any of the arguments are null
*/
- public static String packageNameForAugmentedGeneratedType(final String basePackageName, final SchemaPath schemaPath) {
+ public static String packageNameForAugmentedGeneratedType(final String basePackageName,
+ final SchemaPath schemaPath) {
final int size = Iterables.size(schemaPath.getPathTowardsRoot());
if (size == 0) {
return basePackageName;
return BindingMapping.normalizePackageName(builder.toString());
}
- /**
- * Creates package name from specified <code>basePackageName</code> (package
- * name for module) and <code>schemaPath</code>.
- *
- * Resulting package name is concatenation of <code>basePackageName</code>
- * and all local names of YANG nodes which are parents of some node for
- * which <code>schemaPath</code> is specified.
- *
- * @param basePackageName
- * string with package name of the module
- * @param schemaPath
- * list of names of YANG nodes which are parents of some node +
- * name of this node
- * @param isUsesAugment
- * boolean true if using augment
- * @return string with valid JAVA package name
- *
- * @deprecated Use {@link #packageNameForGeneratedType(String, SchemaPath)} or
- * {@link #packageNameForAugmentedGeneratedType(String, SchemaPath)} instead.
- */
- @Deprecated
- public static String packageNameForGeneratedType(final String basePackageName, final SchemaPath schemaPath,
- final boolean isUsesAugment) {
- if (basePackageName == null) {
- throw new IllegalArgumentException("Base Package Name cannot be NULL!");
- }
- if (schemaPath == null) {
- throw new IllegalArgumentException("Schema Path cannot be NULL!");
- }
-
- final Iterable<QName> iterable = schemaPath.getPathFromRoot();
- final int size = Iterables.size(iterable);
- final int traversalSteps;
- if (isUsesAugment) {
- traversalSteps = size;
- } else {
- traversalSteps = size - 1;
- }
-
- if (traversalSteps == 0) {
- return BindingMapping.normalizePackageName(basePackageName);
- }
-
- return generateNormalizedPackageName(basePackageName, iterable, traversalSteps);
- }
-
- /**
- * Generates the package name for type definition from
- * <code>typeDefinition</code> and <code>basePackageName</code>.
- *
- * @param basePackageName
- * string with the package name of the module
- * @param typeDefinition
- * type definition for which the package name will be generated *
- * @return string with valid JAVA package name
- * @throws IllegalArgumentException
- * <ul>
- * <li>if <code>basePackageName</code> equals <code>null</code></li>
- * <li>if <code>typeDefinition</code> equals <code>null</code></li>
- * </ul>
- * @deprecated This method ignores typeDefinition argument and its result is only
- * <code>BindingMapping.normalizePackageName(basePackageName)</code>.
- * Aside from tests, there is not a single user in OpenDaylight codebase,
- * hence it can be considered buggy and defunct. It is scheduled for removal
- * in Boron release.
- */
- @Deprecated
- public static String packageNameForTypeDefinition(final String basePackageName,
- final TypeDefinition<?> typeDefinition) {
- if (basePackageName == null) {
- throw new IllegalArgumentException("Base Package Name cannot be NULL!");
- }
- if (typeDefinition == null) {
- throw new IllegalArgumentException("Type Definition reference cannot be NULL!");
- }
-
- return BindingMapping.normalizePackageName(basePackageName);
- }
-
- /**
- * Converts <code>token</code> to string which is in accordance with best
- * practices for JAVA class names.
- *
- * @param token
- * string which contains characters which should be converted to
- * JAVA class name
- * @return string which is in accordance with best practices for JAVA class
- * name.
- *
- * @deprecated Use {@link BindingMapping#getClassName(QName)} instead.
- */
- @Deprecated
- public static String parseToClassName(final String token) {
- return parseToCamelCase(token, true);
- }
-
- /**
- * Converts <code>token</code> to string which is in accordance with best
- * practices for JAVA parameter names.
- *
- * @param token
- * string which contains characters which should be converted to
- * JAVA parameter name
- * @return string which is in accordance with best practices for JAVA
- * parameter name.
- *
- * @deprecated Use {@link BindingMapping#getPropertyName(String)} instead.
- */
- @Deprecated public static String parseToValidParamName(final String token) {
- return resolveJavaReservedWordEquivalency(parseToCamelCase(token, false));
- }
-
- /**
- *
- * Converts string <code>token</code> to the cammel case format.
- *
- * @param token
- * string which should be converted to the cammel case format
- * @param uppercase
- * boolean value which says whether the first character of the
- * <code>token</code> should|shuldn't be uppercased
- * @return string in the cammel case format
- * @throws IllegalArgumentException
- * <ul>
- * <li>if <code>token</code> without white spaces is empty</li>
- * <li>if <code>token</code> equals null</li>
- * </ul>
- */
- private static String parseToCamelCase(final String token, final boolean uppercase) {
- if (token == null) {
- throw new IllegalArgumentException("Name can not be null");
- }
-
- String correctStr = DOT_MATCHER.removeFrom(token.trim());
- if (correctStr.isEmpty()) {
- throw new IllegalArgumentException("Name can not be empty");
- }
-
- correctStr = replaceWithCamelCase(correctStr, ' ');
- correctStr = replaceWithCamelCase(correctStr, '-');
- correctStr = replaceWithCamelCase(correctStr, '_');
-
- char firstChar = correctStr.charAt(0);
- firstChar = uppercase ? Character.toUpperCase(firstChar) : Character.toLowerCase(firstChar);
-
- if ((firstChar >= '0') && (firstChar <= '9')) {
- return '_' + correctStr;
- } else {
- return firstChar + correctStr.substring(1);
- }
- }
-
- /**
- * Replaces all the occurrences of the <code>removalChar</code> in the
- * <code>text</code> with empty string and converts following character to
- * upper case.
- *
- * @param text
- * string with source text which should be converted
- * @param removalChar
- * character which is sought in the <code>text</code>
- * @return string which doesn't contain <code>removalChar</code> and has
- * following characters converted to upper case
- * @throws IllegalArgumentException
- * if the length of the returning string has length 0
- */
- private static String replaceWithCamelCase(final String text, final char removalChar) {
- int toBeRemovedPos = text.indexOf(removalChar);
- if (toBeRemovedPos == -1) {
- return text;
- }
-
- final StringBuilder sb = new StringBuilder(text);
- final String toBeRemoved = String.valueOf(removalChar);
- do {
- sb.replace(toBeRemovedPos, toBeRemovedPos + 1, "");
- // check if 'toBeRemoved' character is not the only character in
- // 'text'
- if (sb.length() == 0) {
- throw new IllegalArgumentException("The resulting string can not be empty");
- }
- final char replacement = Character.toUpperCase(sb.charAt(toBeRemovedPos));
- sb.setCharAt(toBeRemovedPos, replacement);
- toBeRemovedPos = sb.indexOf(toBeRemoved);
- } while (toBeRemovedPos != -1);
-
- return sb.toString();
- }
-
private static <T> Iterable<T> sortedCollection(final Comparator<? super T> comparator, final Collection<T> input) {
- if (input.size() > 1) {
- final List<T> ret = new ArrayList<>(input);
- Collections.sort(ret, comparator);
- return ret;
- } else {
+ if (input.size() <= 1) {
return input;
}
+
+ final List<T> ret = new ArrayList<>(input);
+ ret.sort(comparator);
+ return ret;
}
- private static final ThreadLocal<MessageDigest> SHA1_MD = new ThreadLocal<MessageDigest>() {
- @Override
- protected MessageDigest initialValue() {
- try {
- return MessageDigest.getInstance("SHA");
- } catch (final NoSuchAlgorithmException e) {
- throw new IllegalStateException("Failed to get a SHA digest provider", e);
- }
+ private static final ThreadLocal<MessageDigest> SHA1_MD = ThreadLocal.withInitial(() -> {
+ try {
+ return MessageDigest.getInstance("SHA");
+ } catch (final NoSuchAlgorithmException e) {
+ throw new IllegalStateException("Failed to get a SHA digest provider", e);
}
- };
+ });
public static long computeDefaultSUID(final GeneratedTypeBuilderBase<?> to) {
final ByteArrayOutputStream bout = new ByteArrayOutputStream();
- try (final DataOutputStream dout = new DataOutputStream(bout)) {
+ try (DataOutputStream dout = new DataOutputStream(bout)) {
dout.writeUTF(to.getName());
dout.writeInt(to.isAbstract() ? 3 : 7);
- for (final Type ifc : sortedCollection(SUID_NAME_COMPARATOR, to.getImplementsTypes())) {
+ for (final Type ifc : sortedCollection(SUID_NAME_COMPARATOR, filteredImplementsTypes(to))) {
dout.writeUTF(ifc.getFullyQualifiedName());
}
}
for (final MethodSignatureBuilder m : sortedCollection(SUID_MEMBER_COMPARATOR, to.getMethodDefinitions())) {
- if (!(m.getAccessModifier().equals(AccessModifier.PRIVATE))) {
+ if (!m.getAccessModifier().equals(AccessModifier.PRIVATE)) {
dout.writeUTF(m.getName());
dout.write(m.getAccessModifier().ordinal());
}
final byte[] hashBytes = SHA1_MD.get().digest(bout.toByteArray());
long hash = 0;
for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
- hash = (hash << 8) | (hashBytes[i] & 0xFF);
+ hash = hash << 8 | hashBytes[i] & 0xFF;
}
return hash;
}
- private static <T> List<T> currentOrEmpty(final List<T> current, final List<T> base) {
- return current.equals(base) ? ImmutableList.<T>of() : current;
+ private static Collection<Type> filteredImplementsTypes(final GeneratedTypeBuilderBase<?> to) {
+ return Collections2.filter(to.getImplementsTypes(), item -> !BindingTypes.TYPE_OBJECT.equals(item));
+ }
+
+ private static <T extends Optional<?>> T currentOrEmpty(final T current, final T base) {
+ return current.equals(base) ? (T)Optional.empty() : current;
}
private static boolean containsConstraint(final StringTypeDefinition type, final PatternConstraint constraint) {
public static Restrictions getRestrictions(final TypeDefinition<?> type) {
// Old parser generated types which actually contained based restrictions, but our code deals with that when
// binding to core Java types. Hence we'll emit empty restrictions for base types.
- if ((type == null) || (type.getBaseType() == null)) {
+ if (type == null || type.getBaseType() == null) {
// Handling of decimal64 has changed in the new parser. It contains range restrictions applied to the type
// directly, without an extended type. We need to capture such constraints. In order to retain behavior we
// need to analyze the new semantics and see if the constraints have been overridden. To do that we
tmpBuilder.setFractionDigits(decimal.getFractionDigits());
final DecimalTypeDefinition tmp = tmpBuilder.build();
- if (!tmp.getRangeConstraints().equals(decimal.getRangeConstraints())) {
+ if (!tmp.getRangeConstraint().equals(decimal.getRangeConstraint())) {
return new Restrictions() {
@Override
public boolean isEmpty() {
}
@Override
- public List<RangeConstraint> getRangeConstraints() {
- return decimal.getRangeConstraints();
+ public Optional<? extends RangeConstraint<?>> getRangeConstraint() {
+ return decimal.getRangeConstraint();
}
@Override
}
@Override
- public List<LengthConstraint> getLengthConstraints() {
- return ImmutableList.of();
+ public Optional<LengthConstraint> getLengthConstraint() {
+ return Optional.empty();
}
};
}
return EMPTY_RESTRICTIONS;
}
- final List<LengthConstraint> length;
+ final Optional<LengthConstraint> length;
final List<PatternConstraint> pattern;
- final List<RangeConstraint> range;
+ final Optional<? extends RangeConstraint<?>> range;
/*
* Take care of extended types.
if (type instanceof BinaryTypeDefinition) {
final BinaryTypeDefinition binary = (BinaryTypeDefinition)type;
final BinaryTypeDefinition base = binary.getBaseType();
- if ((base != null) && (base.getBaseType() != null)) {
- length = currentOrEmpty(binary.getLengthConstraints(), base.getLengthConstraints());
+ if (base != null && base.getBaseType() != null) {
+ length = currentOrEmpty(binary.getLengthConstraint(), base.getLengthConstraint());
} else {
- length = binary.getLengthConstraints();
+ length = binary.getLengthConstraint();
}
pattern = ImmutableList.of();
- range = ImmutableList.of();
+ range = Optional.empty();
} else if (type instanceof DecimalTypeDefinition) {
- length = ImmutableList.of();
+ length = Optional.empty();
pattern = ImmutableList.of();
final DecimalTypeDefinition decimal = (DecimalTypeDefinition)type;
final DecimalTypeDefinition base = decimal.getBaseType();
- if ((base != null) && (base.getBaseType() != null)) {
- range = currentOrEmpty(decimal.getRangeConstraints(), base.getRangeConstraints());
+ if (base != null && base.getBaseType() != null) {
+ range = currentOrEmpty(decimal.getRangeConstraint(), base.getRangeConstraint());
} else {
- range = decimal.getRangeConstraints();
+ range = decimal.getRangeConstraint();
}
- } else if (type instanceof IntegerTypeDefinition) {
- length = ImmutableList.of();
+ } else if (type instanceof RangeRestrictedTypeDefinition) {
+ // Integer-like types
+ length = Optional.empty();
pattern = ImmutableList.of();
-
- final IntegerTypeDefinition integer = (IntegerTypeDefinition)type;
- final IntegerTypeDefinition base = integer.getBaseType();
- if ((base != null) && (base.getBaseType() != null)) {
- range = currentOrEmpty(integer.getRangeConstraints(), base.getRangeConstraints());
- } else {
- range = integer.getRangeConstraints();
- }
+ range = extractRangeConstraint((RangeRestrictedTypeDefinition<?, ?>)type);
} else if (type instanceof StringTypeDefinition) {
final StringTypeDefinition string = (StringTypeDefinition)type;
final StringTypeDefinition base = string.getBaseType();
- if ((base != null) && (base.getBaseType() != null)) {
- length = currentOrEmpty(string.getLengthConstraints(), base.getLengthConstraints());
+ if (base != null && base.getBaseType() != null) {
+ length = currentOrEmpty(string.getLengthConstraint(), base.getLengthConstraint());
} else {
- length = string.getLengthConstraints();
+ length = string.getLengthConstraint();
}
pattern = uniquePatterns(string);
- range = ImmutableList.of();
- } else if (type instanceof UnsignedIntegerTypeDefinition) {
- length = ImmutableList.of();
- pattern = ImmutableList.of();
-
- final UnsignedIntegerTypeDefinition unsigned = (UnsignedIntegerTypeDefinition)type;
- final UnsignedIntegerTypeDefinition base = unsigned.getBaseType();
- if ((base != null) && (base.getBaseType() != null)) {
- range = currentOrEmpty(unsigned.getRangeConstraints(), base.getRangeConstraints());
- } else {
- range = unsigned.getRangeConstraints();
- }
+ range = Optional.empty();
} else {
- length = ImmutableList.of();
+ length = Optional.empty();
pattern = ImmutableList.of();
- range = ImmutableList.of();
+ range = Optional.empty();
}
// Now, this may have ended up being empty, too...
- if (length.isEmpty() && pattern.isEmpty() && range.isEmpty()) {
+ if (!length.isPresent() && pattern.isEmpty() && !range.isPresent()) {
return EMPTY_RESTRICTIONS;
}
// Nope, not empty allocate a holder
return new Restrictions() {
@Override
- public List<RangeConstraint> getRangeConstraints() {
+ public Optional<? extends RangeConstraint<?>> getRangeConstraint() {
return range;
}
+
@Override
public List<PatternConstraint> getPatternConstraints() {
return pattern;
}
+
@Override
- public List<LengthConstraint> getLengthConstraints() {
+ public Optional<LengthConstraint> getLengthConstraint() {
return length;
}
+
@Override
public boolean isEmpty() {
return false;
};
}
+ private static <T extends RangeRestrictedTypeDefinition<?, ?>> Optional<? extends RangeConstraint<?>>
+ extractRangeConstraint(final T def) {
+ final T base = (T) def.getBaseType();
+ if (base != null && base.getBaseType() != null) {
+ return currentOrEmpty(def.getRangeConstraint(), base.getRangeConstraint());
+ }
+
+ return def.getRangeConstraint();
+ }
+
/**
- * Encodes angle brackets in yang statement description
+ * Encodes angle brackets in yang statement description.
+ *
* @param description description of a yang statement which is used to generate javadoc comments
* @return string with encoded angle brackets
*/
}
return description;
}
+
+ @Deprecated
+ public static String replaceAllIllegalChars(final CharSequence stringBuilder) {
+ return defangUnicodeEscapes(stringBuilder);
+ }
+
+ /**
+ * Escape potential unicode references so that the resulting string is safe to put into a {@code .java} file. This
+ * processing is required to ensure this text we want to append does not end up with eligible backslashes. See
+ * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.3">Java Language Specification</a>
+ * for more information.
+ *
+ * @param str Input string
+ * @return A string with all backslashes made ineligible
+ */
+ public static String replaceAllIllegalChars(final String str) {
+ final int backslash = str.indexOf('\\');
+ return backslash == -1 ? str : defangUnicodeEscapes(str);
+ }
+
+ private static String defangUnicodeEscapes(final CharSequence stringBuilder) {
+ // TODO: we should be able to receive the first offset from the non-deprecated method and perform a manual
+ // check for eligibility and escape -- that would be faster I think.
+ final String ret = UNICODE_CHAR_PATTERN.matcher(stringBuilder).replaceAll("\\\\\\\\u");
+ return ret.isEmpty() ? "" : ret;
+ }
}