All Downloads are FREE. Search and download functionalities are using the official Maven repository.

yakworks.commons.lang.NameUtils.groovy Maven / Gradle / Ivy

/*
* Copyright 2008 original authors
* SPDX-License-Identifier: Apache-2.0
*/
package yakworks.commons.lang

import java.util.regex.Matcher
import java.util.regex.Pattern

import groovy.transform.CompileStatic

import yakworks.commons.util.StringUtils

/**
 * Copied in from grails.util.GrailsNameUtils and converted to groovy
 * Utility methods for converting between different name types,
 * for example from class names -> property names and vice-versa. The
 * key aspect of this class is that it has no dependencies outside stock groovy!
 */
@SuppressWarnings(['ClassSize', 'NestedBlockDepth', 'InvertedIfElse', 'UnnecessaryToString'])
@CompileStatic
class NameUtils {
    private static final Pattern DOT_UPPER = Pattern.compile('\\.[A-Z\\$]');
    private static final String PROPERTY_SET_PREFIX = "set"
    private static final String PROPERTY_GET_PREFIX = "get"
    public static final String DOLLAR_SEPARATOR = '$'
    private static final Pattern SERVICE_ID_REGEX = Pattern.compile('[\\p{javaLowerCase}\\d-]+');
    // private static final Pattern KEBAB_CASE_SEQUENCE = Pattern.compile('^(([a-z0-9])+(\\-|\\.|:)?)*([a-z0-9])+$');
    private static final Pattern KEBAB_REPLACEMENTS = Pattern.compile('[_ ]');

    /**
     * The camel case version of the string with the first letter in lower case.
     *
     * @param str The string
     * @return The new string in camel case
     */
    public static String camelCase(String str) {
        return camelCase(str, true);
    }

    static String toCamelCase( String text ) {
        text = text.toLowerCase().replaceAll("(_)([A-Za-z0-9])") { List it -> it[2].toUpperCase() }
        //println text
        return text
    }

    /**
     * The camel case version of the string with the first letter in lower case.
     *
     * @param str                  The string
     * @param lowerCaseFirstLetter Whether the first letter is in upper case or lower case
     * @return The new string in camel case
     */
    public static String camelCase(String str, boolean lowerCaseFirstLetter) {
        str = str.toLowerCase().replaceAll("(_)([A-Za-z0-9])") { List it -> it[2].toUpperCase() }
        StringBuilder sb = new StringBuilder(str.length());
        for (String s : str.split("[\\s_-]")) {
            String capitalize = capitalize(s);
            sb.append(capitalize);
        }
        String result = sb.toString();
        if (lowerCaseFirstLetter) {
            return decapitalize(result);
        }
        return result;
    }

    /**
     * Retrieves the name of a setter for the specified property name
     * @param propertyName The property name
     * @return The setter equivalent
     */
    static String getSetterName(String propertyName) {
        final String suffix = getSuffixForGetterOrSetter(propertyName);
        return PROPERTY_SET_PREFIX+suffix;
    }

    /**
     * Calculate the name for a getter method to retrieve the specified property
     * @return The name for the getter method for this property, if it were to exist, i.e. getConstraints
     */
    static String getGetterName(String propertyName) {
        final String suffix = getSuffixForGetterOrSetter(propertyName);
        return PROPERTY_GET_PREFIX + suffix;
    }

    static String getSuffixForGetterOrSetter(String propertyName) {
        final String suffix;
        if (propertyName.length() > 1 &&
            Character.isLowerCase(propertyName.charAt(0)) &&
            Character.isUpperCase(propertyName.charAt(1))) {
            suffix = propertyName;
        } else {
            suffix = "${Character.toUpperCase(propertyName.charAt(0))}${propertyName.substring(1)}"
        }
        return suffix;
    }

    /**
     * Returns the class name for the given logical name and trailing name. For example "person" and "Controller" would evaluate to "PersonController"
     *
     * @param logicalName The logical name
     * @param trailingName The trailing name
     * @return The class name
     */
    static String getClassName(String logicalName, String trailingName) {
        if (isBlank(logicalName)) {
            throw new IllegalArgumentException("Argument [logicalName] cannot be null or blank");
        }

        String className = logicalName.substring(0, 1).toUpperCase(Locale.ENGLISH) + logicalName.substring(1);
        if (trailingName != null) {
            className = className + trailingName;
        }
        return className;
    }

    /**
     * Returns the class name, including package, for the given class. This method will deals with proxies and closures.
     *
     * @param cls The class name
     */
    static String getFullClassName(Class cls) {
        String className = cls.getName();

        return getFullClassName(className);
    }

    /**
     * Returns the class name, including package, for the given class. This method will deals with proxies and closures.
     *
     * @param className The class name
     */
    static String getFullClassName(String className) {
        final int i = className.indexOf('$');
        if(i > -1) {
            className = className.substring(0, i);
        }
        return className;
    }

    /**
     * Return the class name for the given logical name. For example "person" would evaluate to "Person"
     *
     * @param logicalName The logical name
     * @return The class name
     */
    static String getClassName(String logicalName) {
        return getClassName(logicalName, "");
    }

    /**
     * Get the class name, taking into account proxies
     *
     * @param clazz The class
     * @return The class name
     */
    static String getClassName(Class clazz) {
        final String sn = clazz.getSimpleName();
        if(sn.contains(DOLLAR_SEPARATOR)) {
            return clazz.getSuperclass().getName();
        }
        return clazz.getName();
    }

    /**
     * Returns the class name representation of the given name
     *
     * @param name The name to convert
     * @return The property name representation
     */
    static String getClassNameRepresentation(String name) {
        if (name == null || name.length() == 0) {
            return "";
        }

        StringBuilder buf = new StringBuilder();
        String[] tokens = name.split("[^\\w\\d]");
        for (String token1 : tokens) {
            String token = token1.trim();
            int length = token.length();
            if (length > 0) {
                buf.append(token.substring(0, 1).toUpperCase(Locale.ENGLISH));
                if (length > 1) {
                    buf.append(token.substring(1));
                }
            }
        }

        return buf.toString();
    }

    /**
     * Converts fooBar into FooBar. Empty and null strings are returned as-is.
     *
     * @param name The lower case hyphen separated name
     * @return The class name equivalent.
     */
    static String capitalize(String name) {
        // Handle null and empty strings.
        if (isBlank(name)) return name

        return name.substring(0, 1).toUpperCase() + name.substring(1)
    }

    /**
     * Converts foo-bar into FooBar. Empty and null strings are returned as-is.
     *
     * @param name The lower case hyphen separated name
     * @return The class name equivalent.
     */
    static String getClassNameFromKebabCase(String name) {
        // Handle null and empty strings.
        if (isBlank(name)) return name;

        if (name.indexOf('-') == -1) {
            return name.substring(0, 1).toUpperCase() + name.substring(1);
        }

        StringBuilder buf = new StringBuilder();
        String[] tokens = name.split("-");
        for (String token : tokens) {
            if (token == null || token.length() == 0) continue;
            buf.append(token.substring(0, 1).toUpperCase())
                .append(token.substring(1));
        }
        return buf.toString();
    }

    /**
     * Shorter version of getPropertyNameRepresentation.
     * @param name The name to convert
     * @return The property name version
     */
    static String getPropertyName(String name) {
        return getPropertyNameRepresentation(name);
    }

    /**
     * Shorter version of getPropertyNameRepresentation.
     * @param clazz The clazz to convert
     * @return The property name version
     */
    static String getPropertyName(Class clazz) {
        return getPropertyNameRepresentation(clazz);
    }

    /**
     * Returns the property name equivalent for the specified class.
     *
     * @param targetClass The class to get the property name for
     * @return A property name reperesentation of the class name (eg. MyClass becomes myClass)
     */
    static String getPropertyNameRepresentation(Class targetClass) {
        return getPropertyNameRepresentation(getShortName(targetClass));
    }

    /**
     * Returns the property name representation of the given name.
     *
     * @param name The name to convert
     * @return The property name representation
     */
    static String getPropertyNameRepresentation(String name) {
        // Strip any package from the name.
        int pos = name.lastIndexOf('.');
        if (pos != -1) {
            name = name.substring(pos + 1);
        }

        if (name.isEmpty()) {
            return name;
        }

        // Check whether the name begins with two upper case letters.
        if (name.length() > 1 && Character.isUpperCase(name.charAt(0)) &&
            Character.isUpperCase(name.charAt(1))) {
            return name;
        }

        String propertyName = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
        if (propertyName.indexOf(' ') > -1) {
            propertyName = propertyName.replaceAll("\\s", "");
        }
        return propertyName;
    }

    /**
     * Converts foo-bar into fooBar.
     *
     * @param name The lower case hyphen separated name
     * @return The property name equivalent
     */
    static String getPropertyNameFromKebabCase(String name) {
        return getPropertyName(getClassNameFromKebabCase(name));
    }

    /**
     * Returns the class name without the package prefix.
     *
     * @param targetClass The class to get a short name for
     * @return The short name of the class
     */
    static String getShortName(Class targetClass) {
        return getShortName(targetClass.getName());
    }

    /**
     * Returns the class name without the package prefix.
     *
     * @param className The class name to get a short name for
     * @return The short name of the class
     */
    static String getShortName(String className) {
        int i = className.lastIndexOf(".");
        if (i > -1) {
            className = className.substring(i + 1, className.length());
        }
        return className;
    }

    /**
     * Returns the class name without the package prefix.
     *
     * @param className The class name to get a short name for
     * @return The short name of the class
     */
    static String getPackageName(String className) {
        int i = className.lastIndexOf(".");
        String packageName = "";
        if (i > -1) {
            packageName = className.substring(0, i);
        }
        return packageName;
    }

    /**
     * Checks whether the given name is a valid service identifier.
     *
     * @param name The name
     * @return True if it is
     */
    public static boolean isHyphenatedLowerCase(String name) {
        return StringUtils.isNotEmpty(name) && SERVICE_ID_REGEX.matcher(name).matches() && Character.isLetter(name.charAt(0));
    }

    /**
     * Converts camel case to hyphenated, lowercase form.
     *
     * @param name The name
     * @return The hyphenated string
     */
    public static String hyphenate(String name) {
        return hyphenate(name, true);
    }

    /**
     * Converts camel case to hyphenated, lowercase form.
     *
     * @param name      The name
     * @param lowerCase Whether the result should be converted to lower case
     * @return The hyphenated string
     */
    public static String hyphenate(String name, boolean lowerCase) {
        if (isHyphenatedLowerCase(name)) {
            return KEBAB_REPLACEMENTS.matcher(name).replaceAll("-");
        } else {
            char separatorChar = '-';
            return separateCamelCase(KEBAB_REPLACEMENTS.matcher(name).replaceAll("-"), lowerCase, separatorChar);
        }
    }

    /**
     * Converts a property name into its natural language equivalent eg ('firstName' becomes 'First Name')
     * @param name The property name to convert
     * @return The converted property name
     */
    static String getNaturalName(String name) {
        //exit fast with what was passed in if its falsy
        if(!name) return name

        name = getShortName(name);
        List words = []
        int i = 0;
        char[] chars = name.toCharArray();
        for (int j = 0; j < chars.length; j++) {
            char c = chars[j];
            String w;
            if (i >= words.size()) {
                w = "";
                words.add(i, w);
            }
            else {
                w = words.get(i);
            }

            if (Character.isLowerCase(c) || Character.isDigit(c)) {
                if (Character.isLowerCase(c) && w.length() == 0) {
                    c = Character.toUpperCase(c);
                }
                else if (w.length() > 1 && Character.isUpperCase(w.charAt(w.length() - 1))) {
                    w = "";
                    words.add(++i, w);
                }

                words.set(i, w + c);
            }
            else if (Character.isUpperCase(c)) {
                if ((i == 0 && w.length() == 0) || (Character.isUpperCase(w.charAt(w.length() - 1)) && Character.isUpperCase(chars[j-1]))) {
                    words.set(i, w + c);
                }
                else {
                    words.add(++i, String.valueOf(c));
                }
            }
        }

        StringBuilder buf = new StringBuilder();
        for (Iterator j = words.iterator(); j.hasNext();) {
            String word = j.next();
            buf.append(word);
            if (j.hasNext()) {
                buf.append(' ');
            }
        }
        return buf.toString();
    }

    /**
     * 

Determines whether a given string is null, empty, * or only contains whitespace. If it contains anything other than * whitespace then the string is not considered to be blank and the * method returns false.

*

We could use Commons Lang for this, but we don't want GrailsNameUtils * to have a dependency on any external library to minimise the number of * dependencies required to bootstrap Grails.

* @param str The string to test. * @return true if the string is null, or * blank. */ static boolean isBlank(String str) { return str == null || str.trim().length() == 0; } /** * Returns an appropriate property name for the given object. If the object is a collection will append List, Set, Collection or Map to the property name * @param object The object * @return The property name convention */ static String getPropertyNameConvention(Object object) { String suffix = ""; return getPropertyNameConvention(object, suffix); } /** * Test whether the give package name is a valid Java package * * @param packageName The name of the package * @return True if it is valid */ static boolean isValidJavaPackage(String packageName) { if(isBlank(packageName)) return false; final String[] parts = packageName.split("\\."); for (String part : parts) { if(!isValidJavaIdentifier(part)) { return false; } } return true; } /** * Test whether the given name is a valid Java identifier * * @param name The name * @return True if it is */ static boolean isValidJavaIdentifier(String name) { if(isBlank(name)) return false; final char[] chars = name.toCharArray(); if(!Character.isJavaIdentifierStart(chars[0])) { return false; } for (char c : chars) { if(!Character.isJavaIdentifierPart(c)) { return false; } } return true; } /** * Returns an appropriate property name for the given object. If the object is a collection will append List, Set, Collection or Map to the property name * @param object The object * @param suffix The suffix to append to the name. * @return The property name convention */ static String getPropertyNameConvention(Object object, String suffix) { if(object != null) { Class type = object.getClass(); if (type.isArray()) { return getPropertyName(type.getComponentType()) + suffix + "Array"; } if (object instanceof Collection) { Collection coll = (Collection) object; if (coll.isEmpty()) { return "emptyCollection"; } Object first = coll.iterator().next(); if(coll instanceof List) { return getPropertyName(first.getClass()) + suffix + "List"; } if(coll instanceof Set) { return getPropertyName(first.getClass()) + suffix + "Set"; } return getPropertyName(first.getClass()) + suffix + "Collection"; } if (object instanceof Map) { Map map = (Map)object; if (map.isEmpty()) { return "emptyMap"; } Object entry = map.values().iterator().next(); if (entry != null) { return getPropertyName(entry.getClass()) + suffix + "Map"; } } else { return getPropertyName(object.getClass()) + suffix; } } return null; } /** * Returns a property name equivalent for the given getter name or null if it is not a valid getter. If not null * or empty the getter name is assumed to be a valid identifier. * * @param getterName The getter name * @return The property name equivalent */ static String getPropertyForGetter(String getterName) { return getPropertyForGetter(getterName, boolean.class); } /** * Returns a property name equivalent for the given getter name and return type or null if it is not a valid getter. If not null * or empty the getter name is assumed to be a valid identifier. * * @param getterName The getter name * @param returnType The type the method returns * @return The property name equivalent */ static String getPropertyForGetter(String getterName, Class returnType) { if (getterName == null || getterName.length() == 0) return null; if (getterName.startsWith("get")) { String prop = getterName.substring(3); return convertValidPropertyMethodSuffix(prop); } if (getterName.startsWith("is") && returnType == boolean.class) { String prop = getterName.substring(2); return convertValidPropertyMethodSuffix(prop); } return null; } /** * This method functions the same as {@link #isPropertyMethodSuffix(String)}, * but in addition returns the property name, or null if not a valid property. * * @param suffix The suffix to inspect * @return The property name or null */ static String convertValidPropertyMethodSuffix(String suffix) { if (suffix.length() == 0) return null; // We assume all characters are Character.isJavaIdentifierPart, but the first one may not be a valid // starting character. if (!Character.isJavaIdentifierStart(suffix.charAt(0))) return null; if (suffix.length() == 1) { return Character.isUpperCase(suffix.charAt(0)) ? suffix.toLowerCase() : null; } if (Character.isUpperCase(suffix.charAt(1))) { // "aProperty", "AProperty" return suffix; } if (Character.isUpperCase(suffix.charAt(0))) { return "${Character.toLowerCase(suffix.charAt(0))}${suffix.substring(1)}" } if('_' == suffix.charAt(0)) { return suffix; } return null; } /** * Returns true if the name of the method specified and the number of arguments make it a javabean property getter. * The name is assumed to be a valid Java method name, that is not verified. * * @param name The name of the method * @param args The arguments * @return true if it is a javabean property getter * @deprecated use {@link #isGetter(String, Class, Class[])} instead because this method has a defect for "is.." method with Boolean return types. */ static boolean isGetter(String name, Class[] args) { return isGetter(name, boolean.class, args); } /** * Returns true if the name of the method specified and the number of arguments make it a javabean property getter. * The name is assumed to be a valid Java method name, that is not verified. * * @param name The name of the method * @param returnType The return type of the method * @param args The arguments * @return true if it is a javabean property getter */ static boolean isGetter(String name, Class returnType, Class[] args) { if (name == null || name.length() == 0 || args == null)return false; if (args.length != 0)return false; if (name.startsWith("get")) { name = name.substring(3); if (isPropertyMethodSuffix(name)) return true; } else if (name.startsWith("is") && returnType == boolean.class) { name = name.substring(2); if (isPropertyMethodSuffix(name)) return true; } return false; } /** * This method is used when interrogating a method name to determine if the * method represents a property getter. For example, if a method is named * getSomeProperty, the value "SomeProperty" could * be passed to this method to determine that the method should be considered * a property getter. Examples of suffixes that would be considered property * getters: *
    *
  • SomeProperty
  • *
  • Word
  • *
  • aProperty
  • *
  • S
  • *
  • X567
  • *
* * Examples of suffixes that would not be considered property getters: *
    *
  • someProperty
  • *
  • word
  • *
  • s
  • *
  • x567
  • *
  • 2other
  • *
  • 5
  • *
* * A suffix like prop from a method getprop() is * not recognized as a valid suffix. However Groovy will recognize such a * method as a property getter but only if a method getProp() or * a property prop does not also exist. The Java Beans * specification is unclear on how to treat such method names, it only says * that "by default" the suffix will start with a capital letter because of * the camel case style usually used. (See the JavaBeans API specification * sections 8.3 and 8.8.) * * This method assumes that all characters in the name are valid Java identifier * letters. * * @param suffix The suffix to inspect * @return true if suffix indicates a property name */ protected static boolean isPropertyMethodSuffix(String suffix) { if(suffix.length() == 0) return false; if(!Character.isJavaIdentifierStart(suffix.charAt(0))) return false; if(suffix.length() == 1) return Character.isUpperCase(suffix.charAt(0)); return Character.isUpperCase(suffix.charAt(0)) || Character.isUpperCase(suffix.charAt(1)); } /** * Returns a property name equivalent for the given setter name or null if it is not a valid setter. If not null * or empty the setter name is assumed to be a valid identifier. * * @param setterName The setter name, must be null or empty or a valid identifier name * @return The property name equivalent */ static String getPropertyForSetter(String setterName) { if (setterName == null || setterName.length() == 0) return null; if (setterName.startsWith("set")) { String prop = setterName.substring(3); return convertValidPropertyMethodSuffix(prop); } return null; } /** * Decapitalizes a given string according to the rule: *
    *
  • If the first or only character is Upper Case, it is made Lower Case *
  • UNLESS the second character is also Upper Case, when the String is * returned unchanged . *
* * @param name The String to decapitalize * @return The decapitalized version of the String */ static String decapitalize(String name) { if (name == null) { return null; } int length = name.length(); if (length == 0) { return name; } // Decapitalizes the first character if a lower case // letter is found within 2 characters after the first // Abc -> abc // AB -> AB // ABC -> ABC // ABc -> aBc boolean firstUpper = Character.isUpperCase(name.charAt(0)); if (firstUpper) { if (length == 1) { return Character.toString(Character.toLowerCase(name.charAt(0))); } for (int i = 1; i < Math.min(length, 3); i++) { if (Character.isLowerCase(name.charAt(i))) { char[] chars = name.toCharArray(); chars[0] = Character.toLowerCase(chars[0]); return new String(chars); } } } return name; } /** * Returns the simple name for a class represented as string. * * @param className The class name * @return The simple name of the class */ public static String getSimpleName(String className) { Matcher matcher = DOT_UPPER.matcher(className); if (matcher.find()) { int position = matcher.start(); return className.substring(position + 1); } return className; } private static String separateCamelCase(String name, boolean lowerCase, char separatorChar) { if (!lowerCase) { StringBuilder newName = new StringBuilder(); boolean first = true; char last = '0'; for (char c : name.toCharArray()) { if (first) { newName.append(c); first = false; } else { if (Character.isUpperCase(c) && !Character.isUpperCase(last)) { if (c != separatorChar) { newName.append(separatorChar); } newName.append(c); } else { if (c == '.') { first = true; } if (c != separatorChar) { if (last == separatorChar) { newName.append(separatorChar); } newName.append(c); } } } last = c; } return newName.toString(); } else { StringBuilder newName = new StringBuilder(); char[] chars = name.toCharArray(); boolean first = true; char last = '0'; char secondLast = separatorChar; for (int i = 0; i < chars.length; i++) { char c = chars[i]; if (Character.isLowerCase(c) || !Character.isLetter(c)) { first = false; if (c != separatorChar) { if (last == separatorChar) { newName.append(separatorChar); } newName.append(c); } } else { char lowerCaseChar = Character.toLowerCase(c); if (first) { first = false; newName.append(lowerCaseChar); } else if (Character.isUpperCase(last) || last == '.') { newName.append(lowerCaseChar); } else if (Character.isDigit(last) && (Character.isUpperCase(secondLast) || secondLast == separatorChar)) { newName.append(lowerCaseChar); } else { newName.append(separatorChar).append(lowerCaseChar); } } if (i > 1) { secondLast = last; } last = c; } return newName.toString(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy