/*
 * Copyright 2002-2020 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 org.springframework.core;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.Iterator;

import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

Provides methods to support various naming and other conventions used throughout the framework. Mainly for internal use within the framework.
Author:Rob Harrop, Juergen Hoeller, Rossen Stoyanchev
Since:2.0
/** * Provides methods to support various naming and other conventions used * throughout the framework. Mainly for internal use within the framework. * * @author Rob Harrop * @author Juergen Hoeller * @author Rossen Stoyanchev * @since 2.0 */
public final class Conventions {
Suffix added to names when using arrays.
/** * Suffix added to names when using arrays. */
private static final String PLURAL_SUFFIX = "List"; private Conventions() { }
Determine the conventional variable name for the supplied Object based on its concrete type. The convention used is to return the un-capitalized short name of the Class, according to JavaBeans property naming rules.

For example:
com.myapp.Product becomes "product"
com.myapp.MyProduct becomes "myProduct"
com.myapp.UKProduct becomes "UKProduct"

For arrays the pluralized version of the array component type is used. For Collections an attempt is made to 'peek ahead' to determine the component type and return its pluralized version.

Params:
  • value – the value to generate a variable name for
Returns:the generated variable name
/** * Determine the conventional variable name for the supplied {@code Object} * based on its concrete type. The convention used is to return the * un-capitalized short name of the {@code Class}, according to JavaBeans * property naming rules. * <p>For example:<br> * {@code com.myapp.Product} becomes {@code "product"}<br> * {@code com.myapp.MyProduct} becomes {@code "myProduct"}<br> * {@code com.myapp.UKProduct} becomes {@code "UKProduct"}<br> * <p>For arrays the pluralized version of the array component type is used. * For {@code Collection}s an attempt is made to 'peek ahead' to determine * the component type and return its pluralized version. * @param value the value to generate a variable name for * @return the generated variable name */
public static String getVariableName(Object value) { Assert.notNull(value, "Value must not be null"); Class<?> valueClass; boolean pluralize = false; if (value.getClass().isArray()) { valueClass = value.getClass().getComponentType(); pluralize = true; } else if (value instanceof Collection) { Collection<?> collection = (Collection<?>) value; if (collection.isEmpty()) { throw new IllegalArgumentException( "Cannot generate variable name for an empty Collection"); } Object valueToCheck = peekAhead(collection); valueClass = getClassForValue(valueToCheck); pluralize = true; } else { valueClass = getClassForValue(value); } String name = ClassUtils.getShortNameAsProperty(valueClass); return (pluralize ? pluralize(name) : name); }
Determine the conventional variable name for the given parameter taking the generic collection type, if any, into account.

As of 5.0 this method supports reactive types:
Mono<com.myapp.Product> becomes "productMono"
Flux<com.myapp.MyProduct> becomes "myProductFlux"
Observable<com.myapp.MyProduct> becomes "myProductObservable"

Params:
  • parameter – the method or constructor parameter
Returns:the generated variable name
/** * Determine the conventional variable name for the given parameter taking * the generic collection type, if any, into account. * <p>As of 5.0 this method supports reactive types:<br> * {@code Mono<com.myapp.Product>} becomes {@code "productMono"}<br> * {@code Flux<com.myapp.MyProduct>} becomes {@code "myProductFlux"}<br> * {@code Observable<com.myapp.MyProduct>} becomes {@code "myProductObservable"}<br> * @param parameter the method or constructor parameter * @return the generated variable name */
public static String getVariableNameForParameter(MethodParameter parameter) { Assert.notNull(parameter, "MethodParameter must not be null"); Class<?> valueClass; boolean pluralize = false; String reactiveSuffix = ""; if (parameter.getParameterType().isArray()) { valueClass = parameter.getParameterType().getComponentType(); pluralize = true; } else if (Collection.class.isAssignableFrom(parameter.getParameterType())) { valueClass = ResolvableType.forMethodParameter(parameter).asCollection().resolveGeneric(); if (valueClass == null) { throw new IllegalArgumentException( "Cannot generate variable name for non-typed Collection parameter type"); } pluralize = true; } else { valueClass = parameter.getParameterType(); ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(valueClass); if (adapter != null && !adapter.getDescriptor().isNoValue()) { reactiveSuffix = ClassUtils.getShortName(valueClass); valueClass = parameter.nested().getNestedParameterType(); } } String name = ClassUtils.getShortNameAsProperty(valueClass); return (pluralize ? pluralize(name) : name + reactiveSuffix); }
Determine the conventional variable name for the return type of the given method, taking the generic collection type, if any, into account.
Params:
  • method – the method to generate a variable name for
Returns:the generated variable name
/** * Determine the conventional variable name for the return type of the * given method, taking the generic collection type, if any, into account. * @param method the method to generate a variable name for * @return the generated variable name */
public static String getVariableNameForReturnType(Method method) { return getVariableNameForReturnType(method, method.getReturnType(), null); }
Determine the conventional variable name for the return type of the given method, taking the generic collection type, if any, into account, falling back on the given actual return value if the method declaration is not specific enough, e.g. Object return type or untyped collection.
Params:
  • method – the method to generate a variable name for
  • value – the return value (may be null if not available)
Returns:the generated variable name
/** * Determine the conventional variable name for the return type of the given * method, taking the generic collection type, if any, into account, falling * back on the given actual return value if the method declaration is not * specific enough, e.g. {@code Object} return type or untyped collection. * @param method the method to generate a variable name for * @param value the return value (may be {@code null} if not available) * @return the generated variable name */
public static String getVariableNameForReturnType(Method method, @Nullable Object value) { return getVariableNameForReturnType(method, method.getReturnType(), value); }
Determine the conventional variable name for the return type of the given method, taking the generic collection type, if any, into account, falling back on the given return value if the method declaration is not specific enough, e.g. Object return type or untyped collection.

As of 5.0 this method supports reactive types:
Mono<com.myapp.Product> becomes "productMono"
Flux<com.myapp.MyProduct> becomes "myProductFlux"
Observable<com.myapp.MyProduct> becomes "myProductObservable"

Params:
  • method – the method to generate a variable name for
  • resolvedType – the resolved return type of the method
  • value – the return value (may be null if not available)
Returns:the generated variable name
/** * Determine the conventional variable name for the return type of the given * method, taking the generic collection type, if any, into account, falling * back on the given return value if the method declaration is not specific * enough, e.g. {@code Object} return type or untyped collection. * <p>As of 5.0 this method supports reactive types:<br> * {@code Mono<com.myapp.Product>} becomes {@code "productMono"}<br> * {@code Flux<com.myapp.MyProduct>} becomes {@code "myProductFlux"}<br> * {@code Observable<com.myapp.MyProduct>} becomes {@code "myProductObservable"}<br> * @param method the method to generate a variable name for * @param resolvedType the resolved return type of the method * @param value the return value (may be {@code null} if not available) * @return the generated variable name */
public static String getVariableNameForReturnType(Method method, Class<?> resolvedType, @Nullable Object value) { Assert.notNull(method, "Method must not be null"); if (Object.class == resolvedType) { if (value == null) { throw new IllegalArgumentException( "Cannot generate variable name for an Object return type with null value"); } return getVariableName(value); } Class<?> valueClass; boolean pluralize = false; String reactiveSuffix = ""; if (resolvedType.isArray()) { valueClass = resolvedType.getComponentType(); pluralize = true; } else if (Collection.class.isAssignableFrom(resolvedType)) { valueClass = ResolvableType.forMethodReturnType(method).asCollection().resolveGeneric(); if (valueClass == null) { if (!(value instanceof Collection)) { throw new IllegalArgumentException("Cannot generate variable name " + "for non-typed Collection return type and a non-Collection value"); } Collection<?> collection = (Collection<?>) value; if (collection.isEmpty()) { throw new IllegalArgumentException("Cannot generate variable name " + "for non-typed Collection return type and an empty Collection value"); } Object valueToCheck = peekAhead(collection); valueClass = getClassForValue(valueToCheck); } pluralize = true; } else { valueClass = resolvedType; ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(valueClass); if (adapter != null && !adapter.getDescriptor().isNoValue()) { reactiveSuffix = ClassUtils.getShortName(valueClass); valueClass = ResolvableType.forMethodReturnType(method).getGeneric().toClass(); } } String name = ClassUtils.getShortNameAsProperty(valueClass); return (pluralize ? pluralize(name) : name + reactiveSuffix); }
Convert Strings in attribute name format (e.g. lowercase, hyphens separating words) into property name format (camel-case). For example transaction-manager becomes "transactionManager".
/** * Convert {@code String}s in attribute name format (e.g. lowercase, hyphens * separating words) into property name format (camel-case). For example * {@code transaction-manager} becomes {@code "transactionManager"}. */
public static String attributeNameToPropertyName(String attributeName) { Assert.notNull(attributeName, "'attributeName' must not be null"); if (!attributeName.contains("-")) { return attributeName; } char[] result = new char[attributeName.length() -1]; // not completely accurate but good guess int currPos = 0; boolean upperCaseNext = false; for (int i = 0; i < attributeName.length(); i++ ) { char c = attributeName.charAt(i); if (c == '-') { upperCaseNext = true; } else if (upperCaseNext) { result[currPos++] = Character.toUpperCase(c); upperCaseNext = false; } else { result[currPos++] = c; } } return new String(result, 0, currPos); }
Return an attribute name qualified by the given enclosing Class. For example the attribute name 'foo' qualified by Class 'com.myapp.SomeClass' would be 'com.myapp.SomeClass.foo'
/** * Return an attribute name qualified by the given enclosing {@link Class}. * For example the attribute name '{@code foo}' qualified by {@link Class} * '{@code com.myapp.SomeClass}' would be '{@code com.myapp.SomeClass.foo}' */
public static String getQualifiedAttributeName(Class<?> enclosingClass, String attributeName) { Assert.notNull(enclosingClass, "'enclosingClass' must not be null"); Assert.notNull(attributeName, "'attributeName' must not be null"); return enclosingClass.getName() + '.' + attributeName; }
Determine the class to use for naming a variable containing the given value.

Will return the class of the given value, except when encountering a JDK proxy, in which case it will determine the 'primary' interface implemented by that proxy.

Params:
  • value – the value to check
Returns:the class to use for naming a variable
/** * Determine the class to use for naming a variable containing the given value. * <p>Will return the class of the given value, except when encountering a * JDK proxy, in which case it will determine the 'primary' interface * implemented by that proxy. * @param value the value to check * @return the class to use for naming a variable */
private static Class<?> getClassForValue(Object value) { Class<?> valueClass = value.getClass(); if (Proxy.isProxyClass(valueClass)) { Class<?>[] ifcs = valueClass.getInterfaces(); for (Class<?> ifc : ifcs) { if (!ClassUtils.isJavaLanguageInterface(ifc)) { return ifc; } } } else if (valueClass.getName().lastIndexOf('$') != -1 && valueClass.getDeclaringClass() == null) { // '$' in the class name but no inner class - // assuming it's a special subclass (e.g. by OpenJPA) valueClass = valueClass.getSuperclass(); } return valueClass; }
Pluralize the given name.
/** * Pluralize the given name. */
private static String pluralize(String name) { return name + PLURAL_SUFFIX; }
Retrieve the Class of an element in the Collection. The exact element for which the Class is retrieved will depend on the concrete Collection implementation.
/** * Retrieve the {@code Class} of an element in the {@code Collection}. * The exact element for which the {@code Class} is retrieved will depend * on the concrete {@code Collection} implementation. */
private static <E> E peekAhead(Collection<E> collection) { Iterator<E> it = collection.iterator(); if (!it.hasNext()) { throw new IllegalStateException( "Unable to peek ahead in non-empty collection - no element found"); } E value = it.next(); if (value == null) { throw new IllegalStateException( "Unable to peek ahead in non-empty collection - only null element found"); } return value; } }