Skip to content

Commit 18ca4ab

Browse files
committed
Polishing.
Refine local variable handling to logical and physical naming where the logical name is used in AOT contributor code while the physical name is rendered. See #3270 Original pull request: #3271
1 parent baf3cb8 commit 18ca4ab

File tree

9 files changed

+123
-180
lines changed

9 files changed

+123
-180
lines changed

src/main/java/org/springframework/data/repository/aot/generate/AotQueryMethodGenerationContext.java

+58-59
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@
2020
import java.util.ArrayList;
2121
import java.util.List;
2222

23-
import javax.lang.model.element.Modifier;
24-
2523
import org.jspecify.annotations.Nullable;
24+
2625
import org.springframework.core.ResolvableType;
2726
import org.springframework.core.annotation.MergedAnnotation;
2827
import org.springframework.core.annotation.MergedAnnotationSelectors;
@@ -31,8 +30,6 @@
3130
import org.springframework.data.repository.query.Parameter;
3231
import org.springframework.data.repository.query.QueryMethod;
3332
import org.springframework.data.repository.query.ReturnedType;
34-
import org.springframework.javapoet.FieldSpec;
35-
import org.springframework.javapoet.ParameterSpec;
3633
import org.springframework.javapoet.TypeName;
3734
import org.springframework.util.ObjectUtils;
3835

@@ -51,7 +48,6 @@ public class AotQueryMethodGenerationContext {
5148
private final RepositoryInformation repositoryInformation;
5249
private final AotRepositoryFragmentMetadata targetTypeMetadata;
5350
private final MethodMetadata targetMethodMetadata;
54-
private final CodeBlocks codeBlocks;
5551
private final VariableNameFactory variableNameFactory;
5652

5753
AotQueryMethodGenerationContext(RepositoryInformation repositoryInformation, Method method, QueryMethod queryMethod,
@@ -64,11 +60,6 @@ public class AotQueryMethodGenerationContext {
6460
this.targetTypeMetadata = targetTypeMetadata;
6561
this.targetMethodMetadata = new MethodMetadata(repositoryInformation, method);
6662
this.variableNameFactory = LocalVariableNameFactory.forMethod(targetMethodMetadata);
67-
this.codeBlocks = new CodeBlocks(targetTypeMetadata);
68-
}
69-
70-
AotRepositoryFragmentMetadata getTargetTypeMetadata() {
71-
return targetTypeMetadata;
7263
}
7364

7465
MethodMetadata getTargetMethodMetadata() {
@@ -79,12 +70,18 @@ public RepositoryInformation getRepositoryInformation() {
7970
return repositoryInformation;
8071
}
8172

82-
public Method getMethod() {
83-
return method;
73+
/**
74+
* Obtain the field name by type.
75+
*
76+
* @param type
77+
* @return
78+
*/
79+
public @Nullable String fieldNameOf(Class<?> type) {
80+
return targetTypeMetadata.fieldNameOf(type);
8481
}
8582

86-
public CodeBlocks codeBlocks() {
87-
return codeBlocks;
83+
public Method getMethod() {
84+
return method;
8885
}
8986

9087
/**
@@ -112,10 +109,18 @@ public ReturnedType getReturnedType() {
112109
return queryMethod.getResultProcessor().getReturnedType();
113110
}
114111

112+
/**
113+
* @return the actual returned domain type.
114+
* @see org.springframework.data.repository.core.RepositoryMetadata#getReturnedDomainClass(Method)
115+
*/
115116
public ResolvableType getActualReturnType() {
116117
return targetMethodMetadata.getActualReturnType();
117118
}
118119

120+
/**
121+
* @return the query method return type.
122+
* @see org.springframework.data.repository.core.RepositoryMetadata#getReturnType(Method)
123+
*/
119124
public ResolvableType getReturnType() {
120125
return targetMethodMetadata.getReturnType();
121126
}
@@ -127,23 +132,13 @@ public TypeName getReturnTypeName() {
127132
return TypeName.get(getReturnType().getType());
128133
}
129134

130-
/**
131-
* Suggest naming clash free variant for the given intended variable name within the local method context.
132-
*
133-
* @param variableName the intended variable name.
134-
* @return the suggested VariableName
135-
*/
136-
public String suggestLocalVariableName(String variableName) {
137-
return variableNameFactory.generateName(variableName);
138-
}
139-
140135
/**
141136
* Returns the required parameter name for the {@link Parameter#isBindable() bindable parameter} at the given
142137
* {@code parameterIndex} or throws {@link IllegalArgumentException} if the parameter cannot be determined by its
143138
* index.
144139
*
145140
* @param parameterIndex the zero-based parameter index as used in the query to reference bindable parameters.
146-
* @return the parameter name.
141+
* @return the method parameter name.
147142
*/
148143
public String getRequiredBindableParameterName(int parameterIndex) {
149144

@@ -161,9 +156,8 @@ public String getRequiredBindableParameterName(int parameterIndex) {
161156
* {@code parameterIndex} or {@code null} if the parameter cannot be determined by its index.
162157
*
163158
* @param parameterIndex the zero-based parameter index as used in the query to reference bindable parameters.
164-
* @return the parameter name.
159+
* @return the method parameter name.
165160
*/
166-
// TODO: Simplify?!
167161
public @Nullable String getBindableParameterName(int parameterIndex) {
168162

169163
int bindable = 0;
@@ -185,12 +179,12 @@ public String getRequiredBindableParameterName(int parameterIndex) {
185179
}
186180

187181
/**
188-
* Returns the required parameter name for the {@link Parameter#isBindable() bindable parameter} at the given
189-
* {@code parameterName} or throws {@link IllegalArgumentException} if the parameter cannot be determined by its
190-
* index.
182+
* Returns the required parameter name for the {@link Parameter#isBindable() bindable parameter} at the given logical
183+
* {@code parameterName} or throws {@link IllegalArgumentException} if the parameter cannot be determined by its name.
191184
*
192185
* @param parameterName the parameter name as used in the query to reference bindable parameters.
193-
* @return the parameter name.
186+
* @return the method parameter name.
187+
* @see org.springframework.data.repository.query.Param
194188
*/
195189
public String getRequiredBindableParameterName(String parameterName) {
196190

@@ -204,13 +198,13 @@ public String getRequiredBindableParameterName(String parameterName) {
204198
}
205199

206200
/**
207-
* Returns the required parameter name for the {@link Parameter#isBindable() bindable parameter} at the given
208-
* {@code parameterName} or {@code null} if the parameter cannot be determined by its index.
201+
* Returns the required parameter name for the {@link Parameter#isBindable() bindable parameter} at the given logical
202+
* {@code parameterName} or {@code null} if the parameter cannot be determined by its name.
209203
*
210204
* @param parameterName the parameter name as used in the query to reference bindable parameters.
211-
* @return the parameter name.
205+
* @return the method parameter name.
206+
* @see org.springframework.data.repository.query.Param
212207
*/
213-
// TODO: Simplify?!
214208
public @Nullable String getBindableParameterName(String parameterName) {
215209

216210
int totalIndex = 0;
@@ -237,7 +231,7 @@ public List<String> getBindableParameterNames() {
237231
List<String> result = new ArrayList<>();
238232

239233
for (Parameter parameter : queryMethod.getParameters().getBindableParameters()) {
240-
getParameterName(parameter.getIndex());
234+
result.add(getParameterName(parameter.getIndex()));
241235
}
242236

243237
return result;
@@ -250,45 +244,50 @@ public List<String> getAllParameterNames() {
250244
return targetMethodMetadata.getMethodArguments().keySet().stream().toList();
251245
}
252246

253-
public boolean hasField(String fieldName) {
254-
return targetTypeMetadata.hasField(fieldName);
255-
}
256-
257-
public void addField(String fieldName, TypeName type, Modifier... modifiers) {
258-
targetTypeMetadata.addField(fieldName, type, modifiers);
259-
}
260-
261-
public void addField(FieldSpec fieldSpec) {
262-
targetTypeMetadata.addField(fieldSpec);
263-
}
264-
265-
public @Nullable String fieldNameOf(Class<?> type) {
266-
return targetTypeMetadata.fieldNameOf(type);
267-
}
268-
269-
@Nullable
270-
public String getParameterNameOf(Class<?> type) {
271-
return targetMethodMetadata.getParameterNameOf(type);
247+
/**
248+
* Obtain a naming-clash free variant for the given logical variable name within the local method context. Returns the
249+
* target variable name when called multiple times with the same {@code variableName}.
250+
*
251+
* @param variableName the logical variable name.
252+
* @return the variable name used in the generated code.
253+
*/
254+
public String localVariable(String variableName) {
255+
return targetMethodMetadata.getLocalVariables().computeIfAbsent(variableName, variableNameFactory::generateName);
272256
}
273257

258+
/**
259+
* Returns the parameter name for the method parameter at {@code position}.
260+
*
261+
* @param position zero-indexed parameter position.
262+
* @return
263+
* @see Method#getParameters()
264+
*/
274265
public @Nullable String getParameterName(int position) {
275266
return targetMethodMetadata.getParameterName(position);
276267
}
277268

278-
public void addParameter(ParameterSpec parameter) {
279-
this.targetMethodMetadata.addParameter(parameter);
280-
}
281-
269+
/**
270+
* @return the parameter name for the {@link org.springframework.data.domain.Sort sort parameter} or {@code null} if
271+
* the method does not declare a sort parameter.
272+
*/
282273
@Nullable
283274
public String getSortParameterName() {
284275
return getParameterName(queryMethod.getParameters().getSortIndex());
285276
}
286277

278+
/**
279+
* @return the parameter name for the {@link org.springframework.data.domain.Pageable pageable parameter} or
280+
* {@code null} if the method does not declare a pageable parameter.
281+
*/
287282
@Nullable
288283
public String getPageableParameterName() {
289284
return getParameterName(queryMethod.getParameters().getPageableIndex());
290285
}
291286

287+
/**
288+
* @return the parameter name for the {@link org.springframework.data.domain.Limit limit parameter} or {@code null} if
289+
* the method does not declare a limit parameter.
290+
*/
292291
@Nullable
293292
public String getLimitParameterName() {
294293
return getParameterName(queryMethod.getParameters().getLimitIndex());

src/main/java/org/springframework/data/repository/aot/generate/AotRepositoryBuilder.java

+15-8
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
*/
5353
class AotRepositoryBuilder {
5454

55+
private static final Log logger = LogFactory.getLog(AotRepositoryBuilder.class);
56+
5557
private final RepositoryInformation repositoryInformation;
5658
private final String moduleName;
5759
private final ProjectionFactory projectionFactory;
@@ -74,7 +76,7 @@ private AotRepositoryBuilder(RepositoryInformation repositoryInformation, String
7476
.initializer("$T.getLog($T.class)", TypeName.get(LogFactory.class), this.generationMetadata.getTargetTypeName())
7577
.build());
7678

77-
this.customizer = (info, metadata, builder) -> {};
79+
this.customizer = (info, builder) -> {};
7880
}
7981

8082
/**
@@ -146,15 +148,21 @@ public AotBundle build() {
146148
return it.getDeclaringClass().getName();
147149
}).thenComparing(Method::getName).thenComparing(Method::getParameterCount).thenComparing(Method::toString))
148150
.forEach(method -> {
149-
contributeMethod(method, repositoryComposition, methodMetadata, builder);
151+
try {
152+
contributeMethod(method, repositoryComposition, methodMetadata, builder);
153+
} catch (RuntimeException e) {
154+
if (logger.isErrorEnabled()) {
155+
logger.error("Failed to contribute Repository method [%s.%s]"
156+
.formatted(repositoryInformation.getRepositoryInterface().getName(), method.getName()), e);
157+
}
158+
}
150159
});
151160

152161
// write fields at the end so we make sure to capture things added by methods
153162
generationMetadata.getFields().values().forEach(builder::addField);
154163

155164
// finally customize the file itself
156-
this.customizer.customize(repositoryInformation, generationMetadata, builder);
157-
165+
this.customizer.customize(repositoryInformation, builder);
158166
JavaFile javaFile = JavaFile.builder(packageName(), builder.build()).build();
159167
AotRepositoryMetadata metadata = getAotRepositoryMetadata(methodMetadata);
160168

@@ -273,11 +281,10 @@ public interface ClassCustomizer {
273281
/**
274282
* Apply customization ot the AOT repository fragment class after it has been defined.
275283
*
276-
* @param information repository information.
277-
* @param metadata metadata of the AOT repository fragment.
278-
* @param builder the actual builder.
284+
* @param information the repository information that is used for the AOT fragment.
285+
* @param builder the class builder to be customized.
279286
*/
280-
void customize(RepositoryInformation information, AotRepositoryFragmentMetadata metadata, TypeSpec.Builder builder);
287+
void customize(RepositoryInformation information, TypeSpec.Builder builder);
281288

282289
}
283290

src/main/java/org/springframework/data/repository/aot/generate/AotRepositoryConstructorBuilder.java

+23-16
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,24 @@
3333
* @author Mark Paluch
3434
* @since 4.0
3535
*/
36-
// TODO: extract constructor contributor in a similar way to MethodContributor.
3736
public class AotRepositoryConstructorBuilder {
3837

3938
private final RepositoryInformation repositoryInformation;
4039
private final AotRepositoryFragmentMetadata metadata;
4140

4241
private ConstructorCustomizer customizer = (info, builder) -> {};
4342

44-
AotRepositoryConstructorBuilder(RepositoryInformation repositoryInformation,
45-
AotRepositoryFragmentMetadata metadata) {
43+
AotRepositoryConstructorBuilder(RepositoryInformation repositoryInformation, AotRepositoryFragmentMetadata metadata) {
4644

4745
this.repositoryInformation = repositoryInformation;
4846
this.metadata = metadata;
4947
}
5048

5149
/**
52-
* Add constructor parameter.
50+
* Add constructor parameter and create a field storing its value.
5351
*
54-
* @param parameterName
55-
* @param type
52+
* @param parameterName name of the parameter.
53+
* @param type parameter type.
5654
*/
5755
public void addParameter(String parameterName, Class<?> type) {
5856

@@ -61,14 +59,15 @@ public void addParameter(String parameterName, Class<?> type) {
6159
addParameter(parameterName, TypeName.get(type));
6260
return;
6361
}
62+
6463
addParameter(parameterName, ParameterizedTypeName.get(type, resolvableType.resolveGenerics()));
6564
}
6665

6766
/**
68-
* Add constructor parameter and create a field for it.
67+
* Add constructor parameter and create a field storing its value.
6968
*
70-
* @param parameterName
71-
* @param type
69+
* @param parameterName name of the parameter.
70+
* @param type parameter type.
7271
*/
7372
public void addParameter(String parameterName, TypeName type) {
7473
addParameter(parameterName, type, true);
@@ -77,13 +76,15 @@ public void addParameter(String parameterName, TypeName type) {
7776
/**
7877
* Add constructor parameter.
7978
*
80-
* @param parameterName
81-
* @param type
79+
* @param parameterName name of the parameter.
80+
* @param type parameter type.
81+
* @param createField whether to create a field for the parameter and assign its value to the field.
8282
*/
8383
public void addParameter(String parameterName, TypeName type, boolean createField) {
8484

8585
this.metadata.addConstructorArgument(parameterName, type, createField ? parameterName : null);
86-
if(createField) {
86+
87+
if (createField) {
8788
this.metadata.addField(parameterName, type, Modifier.PRIVATE, Modifier.FINAL);
8889
}
8990
}
@@ -92,7 +93,7 @@ public void addParameter(String parameterName, TypeName type, boolean createFiel
9293
* Add constructor customizer. Customizer is invoked after adding constructor arguments and before assigning
9394
* constructor arguments to fields.
9495
*
95-
* @param customizer
96+
* @param customizer the customizer with direct access to the {@link MethodSpec.Builder constructor builder}.
9697
*/
9798
public void customize(ConstructorCustomizer customizer) {
9899
this.customizer = customizer;
@@ -109,9 +110,8 @@ MethodSpec buildConstructor() {
109110
customizer.customize(repositoryInformation, builder);
110111

111112
for (Entry<String, ConstructorArgument> parameter : this.metadata.getConstructorArguments().entrySet()) {
112-
if(parameter.getValue().isForLocalField()) {
113-
builder.addStatement("this.$N = $N", parameter.getKey(),
114-
parameter.getKey());
113+
if (parameter.getValue().isForLocalField()) {
114+
builder.addStatement("this.$N = $N", parameter.getKey(), parameter.getKey());
115115
}
116116
}
117117

@@ -123,7 +123,14 @@ MethodSpec buildConstructor() {
123123
*/
124124
public interface ConstructorCustomizer {
125125

126+
/**
127+
* Customize the constructor.
128+
*
129+
* @param information the repository information that is used for the AOT fragment.
130+
* @param builder the constructor builder to be customized.
131+
*/
126132
void customize(RepositoryInformation information, MethodSpec.Builder builder);
133+
127134
}
128135

129136
}

0 commit comments

Comments
 (0)