type, Annotation[] an
return owningType.isMemberClass() && type.getType().equals(owningType.getEnclosingClass());
});
- this.hasSpelExpression = Lazy.of(() -> StringUtils.hasText(getSpelExpression()));
+ this.hasSpelExpression = isNullableMarker ? Lazy.of(false) : Lazy.of(() -> StringUtils.hasText(getSpelExpression()));
+ this.isNullableMarker = isNullableMarker;
}
@Nullable
@@ -87,6 +104,10 @@ private static String getValue(MergedAnnotations annotations) {
.orElse(null);
}
+ public Parameter nullableMarker() {
+ return new Parameter(name, type, annotations, entity, true);
+ }
+
/**
* Returns the name of the parameter.
*
@@ -143,6 +164,10 @@ public boolean hasSpelExpression() {
return this.hasSpelExpression.get();
}
+ public boolean isNullableMarker() {
+ return isNullableMarker;
+ }
+
@Override
public boolean equals(Object o) {
diff --git a/src/main/java/org/springframework/data/mapping/model/ClassGeneratingEntityInstantiator.java b/src/main/java/org/springframework/data/mapping/model/ClassGeneratingEntityInstantiator.java
index 267846cb64..003a024efb 100644
--- a/src/main/java/org/springframework/data/mapping/model/ClassGeneratingEntityInstantiator.java
+++ b/src/main/java/org/springframework/data/mapping/model/ClassGeneratingEntityInstantiator.java
@@ -298,7 +298,7 @@ static , T> Object[] extractInvocationArguments(
int index = 0;
for (Parameter, P> parameter : constructor.getParameters()) {
- params[index++] = provider.getParameterValue(parameter);
+ params[index++] = !parameter.isNullableMarker() ? provider.getParameterValue(parameter) : null;
}
return params;
diff --git a/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java b/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java
index 73e99d225b..9b43c476fc 100644
--- a/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java
+++ b/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java
@@ -16,6 +16,7 @@
package org.springframework.data.mapping.model;
import kotlin.jvm.JvmClassMappingKt;
+import kotlin.jvm.internal.DefaultConstructorMarker;
import kotlin.reflect.KFunction;
import kotlin.reflect.full.KClasses;
import kotlin.reflect.jvm.ReflectJvmMapping;
@@ -195,10 +196,24 @@ > PreferredConstructor discover(TypeInf
}
Constructor javaConstructor = ReflectJvmMapping.getJavaConstructor(primaryConstructor);
-
return javaConstructor != null ? buildPreferredConstructor(javaConstructor, type, entity) : null;
});
}
+
+ @Override
+ > Parameter buildParameter(PersistentEntity owingEntity, String name,
+ TypeInformation> type, Annotation[] annotations) {
+
+ if (name != null) {
+ return super.buildParameter(owingEntity, name, type, annotations);
+ }
+
+ if (ClassUtils.isAssignable(DefaultConstructorMarker.class, type.getType())) {
+ return super.buildParameter(owingEntity, "$defaultConstructorMarker", type, annotations).nullableMarker();
+ }
+
+ throw new IllegalStateException("oh no - cannot build parameter for " + name);
+ }
};
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
@@ -224,8 +239,13 @@ private static Discoverers findDiscoverer(Class> type) {
abstract > PreferredConstructor discover(TypeInformation type,
@Nullable PersistentEntity entity);
+ > Parameter buildParameter(PersistentEntity owingEntity, String name,
+ TypeInformation> type, Annotation[] annotations) {
+ return new Parameter(name, type, annotations, owingEntity);
+ }
+
@SuppressWarnings({ "unchecked", "rawtypes" })
- private static > PreferredConstructor buildPreferredConstructor(
+ > PreferredConstructor buildPreferredConstructor(
Constructor> constructor, TypeInformation typeInformation, @Nullable PersistentEntity entity) {
if (constructor.getParameterCount() == 0) {
@@ -244,7 +264,7 @@ private static > PreferredConstructor b
TypeInformation> type = parameterTypes.get(i);
Annotation[] annotations = parameterAnnotations[i];
- parameters[i] = new Parameter(name, type, annotations, entity);
+ parameters[i] = buildParameter(entity, name, type, annotations);
}
return new PreferredConstructor<>((Constructor) constructor, parameters);
diff --git a/src/test/java/org/springframework/data/mapping/model/ClassGeneratingEntityInstantiatorUnitTests.java b/src/test/java/org/springframework/data/mapping/model/ClassGeneratingEntityInstantiatorUnitTests.java
index e522eef3ce..1f33094dfb 100755
--- a/src/test/java/org/springframework/data/mapping/model/ClassGeneratingEntityInstantiatorUnitTests.java
+++ b/src/test/java/org/springframework/data/mapping/model/ClassGeneratingEntityInstantiatorUnitTests.java
@@ -439,6 +439,33 @@ void entityInstantiatorShouldFailForAbstractClass() {
.createInstance(new BasicPersistentEntity<>(TypeInformation.of(AbstractDto.class)), provider));
}
+ @Test // GH-1947
+ void skipsValueRetrievalForKotlinDefaultConstructorMarker() {
+
+ doReturn(TheTypeConsumingAnInlineClass.class).when(entity).getType();
+ doReturn(PreferredConstructorDiscoverer.discover(TheTypeConsumingAnInlineClass.class))//
+ .when(entity).getInstanceCreatorMetadata();
+
+ ParameterValueProvider valueProvider = new ParameterValueProvider<>() {
+
+ public Object getParameterValue(Parameter parameter) {
+
+ if (parameter.getName() == null || "$defaultConstructorMarker".equals(parameter.getName())) {
+ throw new RuntimeException("Stop, Hammer time! You can't touch this! Break id down!");
+ }
+ return parameter.getName();
+ }
+ };
+
+ Object instance = new ClassGeneratingEntityInstantiator().createInstance(entity, valueProvider);
+ assertThat(instance).isInstanceOf(TheTypeConsumingAnInlineClass.class).satisfies(it -> {
+ assertThat(it.toString()) //
+ .contains("inlineTypeParam=TheActualInlineClass(id=inlineTypeParam)") //
+ .contains("someStringParam=someStringParam") //
+ .contains("id=id");
+ });
+ }
+
private void prepareMocks(Class> type) {
doReturn(type).when(entity).getType();
diff --git a/src/test/kotlin/org/springframework/data/mapping/model/WithInlineClass.kt b/src/test/kotlin/org/springframework/data/mapping/model/WithInlineClass.kt
new file mode 100644
index 0000000000..4b6783aba0
--- /dev/null
+++ b/src/test/kotlin/org/springframework/data/mapping/model/WithInlineClass.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 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 org.springframework.data.mapping.model
+
+inline class TheActualInlineClass(val id: String)
+data class TheTypeConsumingAnInlineClass(val inlineTypeParam: TheActualInlineClass, val someStringParam: String, val id: String)
+