DATACMNS-1322 - Cleanup refactorings.
Add missing since tags. Extract methods to maintain single abstraction level per method. Move visitDefaultValue from ClassGeneratingPropertyAccessorFactory to BytecodeUtil. Extend Javadoc.
This commit is contained in:
committed by
Oliver Gierke
parent
79119000bb
commit
85b507ce10
@@ -114,10 +114,27 @@ public interface PersistentProperty<P extends PersistentProperty<P>> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the wither method to set a property value on a new object instance. Might return {@literal null} in case
|
||||
* there is no wither available.
|
||||
* Returns the wither {@link Method} to set a property value on a new object instance. Might return {@literal null} in
|
||||
* case there is no wither available.
|
||||
* <p/>
|
||||
* Wither {@link Method methods} are property-bound instance {@link Method methods} that accept a single argument of
|
||||
* the property type creating a new object instance.
|
||||
*
|
||||
* @return the wither method to set a property value on a new object instance if available, otherwise {@literal null}.
|
||||
* <pre class="code">
|
||||
* class Person {
|
||||
* final String id;
|
||||
* final String name;
|
||||
*
|
||||
* // …
|
||||
*
|
||||
* Person withName(String name) {
|
||||
* return new Person(this.id, name);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return the wither {@link Method} to set a property value on a new object instance if available, otherwise
|
||||
* {@literal null}.
|
||||
* @since 2.1
|
||||
*/
|
||||
@Nullable
|
||||
@@ -247,6 +264,7 @@ public interface PersistentProperty<P extends PersistentProperty<P>> {
|
||||
* {@code final}.
|
||||
*
|
||||
* @return
|
||||
* @see java.lang.reflect.Modifier#isFinal(int)
|
||||
* @since 2.1
|
||||
*/
|
||||
boolean isImmutable();
|
||||
@@ -366,7 +384,7 @@ public interface PersistentProperty<P extends PersistentProperty<P>> {
|
||||
|
||||
/**
|
||||
* Return the type the property refers to in case it's an association.
|
||||
*
|
||||
*
|
||||
* @return the type the property refers to in case it's an association or {@literal null} in case it's not an
|
||||
* association, the target entity type is not explicitly defined (either explicitly or through the property
|
||||
* type itself).
|
||||
|
||||
@@ -123,7 +123,8 @@ public interface PersistentPropertyAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying bean. May change between {@link #setProperty(PersistentProperty, Object)} calls.
|
||||
* Returns the underlying bean. The actual instance may change between
|
||||
* {@link #setProperty(PersistentProperty, Object)} calls.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
|
||||
@@ -160,37 +160,62 @@ class BeanWrapper<T> implements PersistentPropertyAccessor {
|
||||
|
||||
/**
|
||||
* Utility class to leverage Kotlin's copy method for immutable data classes.
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
static class KotlinCopyUtil {
|
||||
|
||||
/**
|
||||
* Set a single property by calling {@code copy(…)} on a Kotlin data class. Copying creates a new instance that
|
||||
* holds all values of the original instance and the newly set {@link PersistentProperty} value.
|
||||
*
|
||||
* @see KCallable#callBy(Map)
|
||||
*/
|
||||
static <T> Object setProperty(PersistentProperty<?> property, T bean, @Nullable Object value) {
|
||||
|
||||
Class<?> type = property.getOwner().getType();
|
||||
KClass<?> kotlinClass = JvmClassMappingKt.getKotlinClass(type);
|
||||
KCallable<?> copy = getCopyMethod(kotlinClass);
|
||||
|
||||
if (copy == null) {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"Kotlin class %s has no .copy(…) method. Cannot set property %s!", type.getName(), property.getName()));
|
||||
}
|
||||
|
||||
return copy.callBy(getCallArgs(copy, property, bean, value));
|
||||
}
|
||||
|
||||
private static <T> Map<KParameter, Object> getCallArgs(KCallable<?> callable, PersistentProperty<?> property,
|
||||
T bean, @Nullable Object value) {
|
||||
|
||||
Map<KParameter, Object> args = new LinkedHashMap<>(2, 1);
|
||||
for (KCallable<?> kCallable : kotlinClass.getMembers()) {
|
||||
|
||||
List<KParameter> parameters = kCallable.getParameters();
|
||||
List<KParameter> parameters = callable.getParameters();
|
||||
|
||||
for (KParameter parameter : parameters) {
|
||||
for (KParameter parameter : parameters) {
|
||||
|
||||
if (parameter.getKind() == Kind.INSTANCE) {
|
||||
args.put(parameter, bean);
|
||||
}
|
||||
|
||||
if (parameter.getKind() == Kind.VALUE && parameter.getName().equals(property.getName())) {
|
||||
args.put(parameter, value);
|
||||
}
|
||||
if (parameter.getKind() == Kind.INSTANCE) {
|
||||
args.put(parameter, bean);
|
||||
}
|
||||
|
||||
if (parameter.getKind() == Kind.VALUE && parameter.getName().equals(property.getName())) {
|
||||
args.put(parameter, value);
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static KCallable<?> getCopyMethod(KClass<?> kotlinClass) {
|
||||
|
||||
for (KCallable<?> kCallable : kotlinClass.getMembers()) {
|
||||
|
||||
if (kCallable.getName().equals("copy")) {
|
||||
return kCallable.callBy(args);
|
||||
return kCallable;
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"Kotlin class %s has no .copy(…) method. Cannot set property %s!", type.getName(), property.getName()));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,14 @@ import lombok.experimental.UtilityClass;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.asm.Opcodes;
|
||||
import org.springframework.asm.Type;
|
||||
|
||||
/**
|
||||
* Utility methods used for ASM-based class generation during runtime.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
@UtilityClass
|
||||
class BytecodeUtil {
|
||||
@@ -81,7 +83,7 @@ class BytecodeUtil {
|
||||
*
|
||||
* @param in the input type
|
||||
* @param out the expected output type
|
||||
* @param visitor
|
||||
* @param visitor must not be {@literal null}.
|
||||
*/
|
||||
static void autoboxIfNeeded(Class<?> in, Class<?> out, MethodVisitor visitor) {
|
||||
|
||||
@@ -154,7 +156,7 @@ class BytecodeUtil {
|
||||
* Checks whether the class is accessible by inspecting modifiers (i.e. whether the class is {@code private}).
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
* @return {@literal true} if the {@link Class} is accessible.
|
||||
* @see Modifier#isPrivate(int)
|
||||
*/
|
||||
static boolean isAccessible(Class<?> type) {
|
||||
@@ -165,7 +167,7 @@ class BytecodeUtil {
|
||||
* Checks whether the class is accessible by inspecting modifiers (i.e. whether the class is {@code private}).
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
* @return {@literal true} if the {@code modifiers} do not indicate the private flag.
|
||||
* @see Modifier#isPrivate(int)
|
||||
*/
|
||||
static boolean isAccessible(int modifiers) {
|
||||
@@ -177,7 +179,7 @@ class BytecodeUtil {
|
||||
* {@literal private}/{@literal protected}/{@literal public}).
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
* @return {@literal true} if the {@code modifiers} indicate {@literal default}.
|
||||
* @see Modifier#isPrivate(int)
|
||||
*/
|
||||
static boolean isDefault(int modifiers) {
|
||||
@@ -188,12 +190,18 @@ class BytecodeUtil {
|
||||
* Create a reference type name in the form of {@literal Ljava/lang/Object;}.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
* @return reference type name in the form of {@literal Ljava/lang/Object;}.
|
||||
*/
|
||||
static String referenceName(Class<?> type) {
|
||||
return type.isArray() ? Type.getInternalName(type) : referenceName(Type.getInternalName(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reference type name in the form of {@literal Ljava/lang/Object;}.
|
||||
*
|
||||
* @param internalTypeName must not be {@literal null}.
|
||||
* @return reference type name in the form of {@literal Ljava/lang/Object;}.
|
||||
*/
|
||||
static String referenceName(String internalTypeName) {
|
||||
return String.format("L%s;", internalTypeName);
|
||||
}
|
||||
@@ -244,4 +252,39 @@ class BytecodeUtil {
|
||||
|
||||
return referenceName(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a byte code instruction that puts a default value for {@link Class} on the stack. Primitive types default to
|
||||
* zero, reference types use {@literal null}.
|
||||
*
|
||||
* @param parameterType must not be {@literal null}.
|
||||
* @param mv must not be {@literal null}.
|
||||
*/
|
||||
static void visitDefaultValue(Class<?> parameterType, MethodVisitor mv) {
|
||||
|
||||
if (parameterType.isPrimitive()) {
|
||||
|
||||
if (parameterType == Integer.TYPE || parameterType == Short.TYPE || parameterType == Boolean.TYPE) {
|
||||
mv.visitInsn(Opcodes.ICONST_0);
|
||||
}
|
||||
|
||||
if (parameterType == Long.TYPE) {
|
||||
mv.visitInsn(Opcodes.LCONST_0);
|
||||
}
|
||||
|
||||
if (parameterType == Double.TYPE) {
|
||||
mv.visitInsn(Opcodes.DCONST_0);
|
||||
}
|
||||
|
||||
if (parameterType == Float.TYPE) {
|
||||
mv.visitInsn(Opcodes.FCONST_0);
|
||||
}
|
||||
|
||||
if (parameterType == Character.TYPE || parameterType == Byte.TYPE) {
|
||||
mv.visitIntInsn(Opcodes.BIPUSH, 0);
|
||||
}
|
||||
} else {
|
||||
mv.visitInsn(Opcodes.ACONST_NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -83,7 +83,8 @@ public class Property {
|
||||
|
||||
private static Optional<Method> findWither(TypeInformation<?> owner, String propertyName, Class<?> rawType) {
|
||||
|
||||
Method method = ReflectionUtils.findMethod(owner.getType(), "with" + StringUtils.capitalize(propertyName), rawType);
|
||||
Method method = ReflectionUtils.findMethod(owner.getType(),
|
||||
String.format("with%s", StringUtils.capitalize(propertyName)), rawType);
|
||||
|
||||
if (method != null && owner.isAssignableFrom(owner.getReturnType(method))) {
|
||||
return Optional.of(method);
|
||||
@@ -147,7 +148,7 @@ public class Property {
|
||||
*/
|
||||
public static boolean supportsStandalone(PropertyDescriptor descriptor) {
|
||||
|
||||
Assert.notNull(descriptor, "PropertDescriptor must not be null!");
|
||||
Assert.notNull(descriptor, "PropertyDescriptor must not be null!");
|
||||
|
||||
return descriptor.getPropertyType() != null;
|
||||
}
|
||||
|
||||
@@ -410,6 +410,7 @@ public class ReflectionUtils {
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return boxed primitive default value.
|
||||
* @since 2.1
|
||||
*/
|
||||
public static Object getPrimitiveDefault(Class<?> type) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user