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

grails.util.GrailsNameUtils Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2008-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package grails.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

/**
 * 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 the JDK!
 */
public final class GrailsNameUtils {

    private static final String PROPERTY_SET_PREFIX = "set";

    private static final String PROPERTY_GET_PREFIX = "get";

    private GrailsNameUtils() {
    }

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

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

    private static String getSuffixForGetterOrSetter(String propertyName) {
        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
     */
    public 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
     */
    public 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
     */
    public static String getFullClassName(String className) {
        int i = className.indexOf('$');
        if (i > -1) {
            return 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
     */
    public static String getClassName(String logicalName) {
        return getClassName(logicalName, "");
    }

    /**
     * Returns the class name representation of the given name
     *
     * @param name The name to convert
     * @return The property name representation
     */
    public 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 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.
     */
    private static String getClassNameForLowerCaseHyphenSeparatedName(String name) {
        // Handle null and empty strings.
        if (isBlank(name)) {
            return name;
        }

        if (!name.contains("-")) {
            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();
    }

    /**
     * Retrieves the logical class name of a Grails artifact given the Grails class
     * and a specified trailing name.
     *
     * @param clazz The class
     * @param trailingName The trailing name such as "Controller" or "TagLib"
     * @return The logical class name
     */
    public static String getLogicalName(Class clazz, String trailingName) {
        return getLogicalName(clazz.getName(), trailingName);
    }

    /**
     * Retrieves the logical name of the class without the trailing name
     * @param name The name of the class
     * @param trailingName The trailing name
     * @return The logical name
     */
    public static String getLogicalName(String name, String trailingName) {
        if (isBlank(trailingName)) {
            return name;
        }

        String shortName = getShortName(name);
        if (!shortName.contains(trailingName)) {
            return name;
        }

        return shortName.substring(0, shortName.length() - trailingName.length());
    }

    public static String getLogicalPropertyName(Class clazz, String trailingName) {
        return getLogicalPropertyName(clazz.getName(), trailingName);
    }

    public static String getLogicalPropertyName(String className, String trailingName) {
        if (!isBlank(className) && !isBlank(trailingName)) {
            if (className.length() == trailingName.length() + 1 && className.endsWith(trailingName)) {
                return className.substring(0, 1).toLowerCase();
            }
        }
        return getLogicalName(getPropertyName(className), trailingName);
    }

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

    /**
     * Shorter version of getPropertyNameRepresentation.
     * @param clazz The clazz to convert
     * @return The property name version
     */
    public 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)
     */
    public 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
     */
    public 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.contains(" ")) {
            propertyName = propertyName.replaceAll("\\s", "");
        }
        return propertyName;
    }

    /**
     * Converts foo-bar into fooBar.
     *
     * @param name The lower case hyphen separated name
     * @return The property name equivalent
     */
    public static String getPropertyNameForLowerCaseHyphenSeparatedName(String name) {
        return getPropertyName(getClassNameForLowerCaseHyphenSeparatedName(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
     */
    public 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
     */
    public static String getShortName(String className) {
        if (className == null) {
            return null;
        }
        int i = className.lastIndexOf(".");
        if (i > -1) {
            return className.substring(i + 1);
        }
        return className;
    }

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

    /**
     * Retrieves the script name representation of the supplied class. For example
     * MyFunkyGrailsScript would be my-funky-grails-script.
     *
     * @param clazz The class to convert
     * @return The script name representation
     */
    public static String getScriptName(Class clazz) {
        return clazz == null ? null : getScriptName(clazz.getName());
    }

    /**
     * Retrieves the script name representation of the given class name.
     * For example MyFunkyGrailsScript would be my-funky-grails-script.
     *
     * @param name The class name to convert.
     * @return The script name representation.
     */
    public static String getScriptName(String name) {
        if (name == null) {
            return null;
        }

        if (name.endsWith(".groovy")) {
            name = name.substring(0, name.length() - 7);
        }
        return getNaturalName(name).replaceAll("\\s", "-").toLowerCase();
    }

    /**
     * Retrieves the snake case name of the supplied class.
     * For example MyFunkyGrailsScript would be my_funky_grails_script.
     *
     * @param clazz The class to convert
     * @return The script name representation
     */
    public static String getSnakeCaseName(Class clazz) {
        return clazz == null ? null : getSnakeCaseName(clazz.getName());
    }

    /**
     * Retrieves the snake case name of the given class name.
     * For example MyFunkyGrailsScript would be my_funky_grails_script.
     *
     * @param name The class name to convert.
     * @return The snake case name representation.
     */
    public static String getSnakeCaseName(String name) {
        if (name == null) {
            return null;
        }

        if (name.endsWith(".groovy")) {
            name = name.substring(0, name.length() - 7);
        }
        return getNaturalName(name).replaceAll("\\s", "_").toLowerCase();
    }

    /**
     * Calculates the class name from a script name in the form my-funk-grails-script.
     *
     * @param scriptName The script name
     * @return A class name
     */
    public static String getNameFromScript(String scriptName) {
        return getClassNameForLowerCaseHyphenSeparatedName(scriptName);
    }

    /**
     * Returns the name of a plugin given the name of the *GrailsPlugin.groovy
     * descriptor file. For example, "DbUtilsGrailsPlugin.groovy" gives
     * "db-utils".
     * @param descriptorName The simple name of the plugin descriptor.
     * @return The plugin name for the descriptor, or null
     * if descriptorName is null, or an empty string
     * if descriptorName is an empty string.
     * @throws IllegalArgumentException if the given descriptor name is
     * not valid, i.e. if it doesn't end with "GrailsPlugin.groovy".
     */
    public static String getPluginName(String descriptorName) {
        if (descriptorName == null || descriptorName.length() == 0) {
            return descriptorName;
        }

        if (!descriptorName.endsWith("GrailsPlugin.groovy")) {
            throw new IllegalArgumentException("Plugin descriptor name is not valid: " + descriptorName);
        }

        return getScriptName(descriptorName.substring(0, descriptorName.indexOf("GrailsPlugin.groovy")));
    }

    /**
     * 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
     */
    public static String getNaturalName(String name) {
        name = getShortName(name);
        List words = new ArrayList<>();
        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. */ public 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 */ public 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 */ public static boolean isValidJavaPackage(String packageName) { if (isBlank(packageName)) { return false; } 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 */ public static boolean isValidJavaIdentifier(String name) { if (isBlank(name)) { return false; } 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 */ @SuppressWarnings("rawtypes") public 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 */ public 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 */ public static String getPropertyForGetter(String getterName, Class returnType) { return getPropertyForGetter(getterName, returnType.getName()); } /** * 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 */ public static String getPropertyForGetter(String getterName, String 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.equals("boolean")) { 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. */ @Deprecated public 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 */ public 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); return isPropertyMethodSuffix(name); } else if (name.startsWith("is") && returnType == boolean.class) { name = name.substring(2); return isPropertyMethodSuffix(name); } 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 */ 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 */ public 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy