19
19
import java .lang .reflect .Method ;
20
20
import java .nio .charset .StandardCharsets ;
21
21
import java .util .Collections ;
22
+ import java .util .List ;
22
23
23
24
import org .apache .commons .logging .Log ;
24
25
import org .apache .commons .logging .LogFactory ;
38
39
import org .springframework .data .repository .query .QueryMethod ;
39
40
import org .springframework .javapoet .JavaFile ;
40
41
import org .springframework .javapoet .TypeSpec ;
42
+ import org .springframework .util .StringUtils ;
41
43
42
44
/**
43
45
* Contributor for AOT repository fragments.
49
51
public class RepositoryContributor {
50
52
51
53
private static final Log logger = LogFactory .getLog (RepositoryContributor .class );
54
+ private static final Log jsonLogger = LogFactory .getLog (RepositoryContributor .class .getName () + ".json" );
52
55
private static final String FEATURE_NAME = "AotRepository" ;
53
56
54
57
private final AotRepositoryContext repositoryContext ;
@@ -58,7 +61,7 @@ public class RepositoryContributor {
58
61
/**
59
62
* Create a new {@code RepositoryContributor} for the given {@link AotRepositoryContext}.
60
63
*
61
- * @param repositoryContext
64
+ * @param repositoryContext context providing details about the repository to be generated.
62
65
*/
63
66
public RepositoryContributor (AotRepositoryContext repositoryContext ) {
64
67
@@ -144,29 +147,35 @@ public final void contribute(GenerationContext generationContext) {
144
147
// write out the content
145
148
AotBundle aotBundle = creator .create (targetTypeSpec );
146
149
147
- String repositoryJson = generateJsonMetadata (aotBundle );
150
+ String repositoryJson = repositoryContext .isGeneratedRepositoriesMetadataEnabled ()
151
+ ? generateJsonMetadata (aotBundle )
152
+ : null ;
148
153
149
154
if (logger .isTraceEnabled ()) {
150
155
151
- if (repositoryContext .isGeneratedRepositoriesMetadataEnabled ()) {
152
- logger .trace ("""
153
- ------ AOT Repository.json: %s ------
154
- %s
155
- -------------------
156
- """ .formatted (aotBundle .repositoryJsonFileName (), repositoryJson ));
157
- }
158
-
159
156
TypeSpec typeSpec = targetTypeSpec .build ();
160
157
JavaFile javaFile = JavaFile .builder (creator .packageName (), typeSpec ).build ();
161
158
162
159
logger .trace ("""
163
- ------ AOT Generated Repository: %s ------
160
+
164
161
%s
165
- -------------------
166
- """ .formatted (typeSpec .name (), javaFile ));
162
+ """ .formatted (formatTraceMessage ("Generated Repository" , typeSpec .name (),
163
+ prefixWithLineNumbers (javaFile .toString ()).trim ())));
164
+ }
165
+
166
+ if (jsonLogger .isTraceEnabled ()) {
167
+
168
+ if (StringUtils .hasText (repositoryJson )) {
169
+
170
+ jsonLogger .trace ("""
171
+
172
+ %s
173
+ """ .formatted (
174
+ formatTraceMessage ("Repository.json" , aotBundle .repositoryJsonFileName (), repositoryJson )));
175
+ }
167
176
}
168
177
169
- if (repositoryContext . isGeneratedRepositoriesMetadataEnabled ( )) {
178
+ if (StringUtils . hasText ( repositoryJson )) {
170
179
generationContext .getGeneratedFiles ().handleFile (Kind .RESOURCE , aotBundle .repositoryJsonFileName (),
171
180
fileHandler -> {
172
181
if (!fileHandler .exists ()) {
@@ -185,6 +194,59 @@ public final void contribute(GenerationContext generationContext) {
185
194
MemberCategory .INVOKE_DECLARED_CONSTRUCTORS , MemberCategory .INVOKE_PUBLIC_METHODS );
186
195
}
187
196
197
+ /**
198
+ * Format a trace message with a title, label, and content using ascii art style borders.
199
+ *
200
+ * @param title title of the block (e.g. "Generated Source").
201
+ * @param label label that follows the title. Will be truncated if too long.
202
+ * @param content the actual content to be displayed.
203
+ * @return
204
+ */
205
+ public static String formatTraceMessage (String title , String label , String content ) {
206
+
207
+ int remainingLength = 64 - title .length ();
208
+ String header = ("= %s: %-" + remainingLength + "." + remainingLength + "s =" ).formatted (title ,
209
+ formatMaxLength (label , remainingLength - 1 ));
210
+
211
+ return """
212
+ ======================================================================
213
+ %s
214
+ ======================================================================
215
+ %s
216
+ ======================================================================
217
+ """ .formatted (header , content );
218
+ }
219
+
220
+ private static String formatMaxLength (String name , int length ) {
221
+ return name .length () > length ? "…" + name .substring (name .length () - length ) : name ;
222
+ }
223
+
224
+ /**
225
+ * Format the given contents by prefixing each line with its line number in a block comment.
226
+ *
227
+ * @param contents
228
+ * @return
229
+ */
230
+ public static String prefixWithLineNumbers (String contents ) {
231
+
232
+ List <String > lines = contents .lines ().toList ();
233
+
234
+ int decimals = (int ) Math .log10 (Math .abs (lines .size ())) + 1 ;
235
+ StringBuilder builder = new StringBuilder ();
236
+
237
+ int lineNumber = 1 ;
238
+ for (String s : lines ) {
239
+
240
+ String formattedLineNumber = String .format ("/* %-" + decimals + "d */\t " , lineNumber );
241
+
242
+ builder .append (formattedLineNumber ).append (s ).append (System .lineSeparator ());
243
+
244
+ lineNumber ++;
245
+ }
246
+
247
+ return builder .toString ();
248
+ }
249
+
188
250
private String generateJsonMetadata (AotBundle aotBundle ) {
189
251
190
252
String repositoryJson = "" ;
0 commit comments