1919import java .lang .reflect .Method ;
2020import java .nio .charset .StandardCharsets ;
2121import java .util .Collections ;
22+ import java .util .List ;
2223
2324import org .apache .commons .logging .Log ;
2425import org .apache .commons .logging .LogFactory ;
3839import org .springframework .data .repository .query .QueryMethod ;
3940import org .springframework .javapoet .JavaFile ;
4041import org .springframework .javapoet .TypeSpec ;
42+ import org .springframework .util .StringUtils ;
4143
4244/**
4345 * Contributor for AOT repository fragments.
4951public class RepositoryContributor {
5052
5153 private static final Log logger = LogFactory .getLog (RepositoryContributor .class );
54+ private static final Log jsonLogger = LogFactory .getLog (RepositoryContributor .class .getName () + ".json" );
5255 private static final String FEATURE_NAME = "AotRepository" ;
5356
5457 private final AotRepositoryContext repositoryContext ;
@@ -58,7 +61,7 @@ public class RepositoryContributor {
5861 /**
5962 * Create a new {@code RepositoryContributor} for the given {@link AotRepositoryContext}.
6063 *
61- * @param repositoryContext
64+ * @param repositoryContext context providing details about the repository to be generated.
6265 */
6366 public RepositoryContributor (AotRepositoryContext repositoryContext ) {
6467
@@ -144,29 +147,35 @@ public final void contribute(GenerationContext generationContext) {
144147 // write out the content
145148 AotBundle aotBundle = creator .create (targetTypeSpec );
146149
147- String repositoryJson = generateJsonMetadata (aotBundle );
150+ String repositoryJson = repositoryContext .isGeneratedRepositoriesMetadataEnabled ()
151+ ? generateJsonMetadata (aotBundle )
152+ : null ;
148153
149154 if (logger .isTraceEnabled ()) {
150155
151- if (repositoryContext .isGeneratedRepositoriesMetadataEnabled ()) {
152- logger .trace ("""
153- ------ AOT Repository.json: %s ------
154- %s
155- -------------------
156- """ .formatted (aotBundle .repositoryJsonFileName (), repositoryJson ));
157- }
158-
159156 TypeSpec typeSpec = targetTypeSpec .build ();
160157 JavaFile javaFile = JavaFile .builder (creator .packageName (), typeSpec ).build ();
161158
162159 logger .trace ("""
163- ------ AOT Generated Repository: %s ------
160+
164161 %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+ }
167176 }
168177
169- if (repositoryContext . isGeneratedRepositoriesMetadataEnabled ( )) {
178+ if (StringUtils . hasText ( repositoryJson )) {
170179 generationContext .getGeneratedFiles ().handleFile (Kind .RESOURCE , aotBundle .repositoryJsonFileName (),
171180 fileHandler -> {
172181 if (!fileHandler .exists ()) {
@@ -185,6 +194,59 @@ public final void contribute(GenerationContext generationContext) {
185194 MemberCategory .INVOKE_DECLARED_CONSTRUCTORS , MemberCategory .INVOKE_PUBLIC_METHODS );
186195 }
187196
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+
188250 private String generateJsonMetadata (AotBundle aotBundle ) {
189251
190252 String repositoryJson = "" ;
0 commit comments