When performance tracking is disabled, returns a {@link NoOpPerformanceTracker} that has + * zero overhead. When enabled via {@code -Asimplebuilder.performanceTracking=true}, returns an + * {@link ActivePerformanceTracker} that measures execution times. + * + * @return the performance tracker instance + */ + public PerformanceTracker getPerformanceTracker() { + return performanceTracker; + } + /** * Get the TypeElement for a given qualified class name. * @@ -229,6 +253,15 @@ public java.util.List extends TypeMirror> directSupertypes(TypeMirror typeMirr return typeUtils.directSupertypes(typeMirror); } + /** + * Gets the processing logger for this context. + * + * @return the processing logger + */ + public ProcessingLogger getLogger() { + return logger; + } + /** * Logs an info-level message that appears in normal Maven output. * diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/processing/logging/ActivePerformanceTracker.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/processing/logging/ActivePerformanceTracker.java new file mode 100644 index 00000000..53a4d419 --- /dev/null +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/processing/logging/ActivePerformanceTracker.java @@ -0,0 +1,304 @@ +/* + * MIT License + * + * Copyright (c) 2026 Andreas Igel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.javahelpers.simple.builders.processor.processing.logging; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** + * Active implementation of {@link PerformanceTracker} that measures execution times using {@link + * System#nanoTime()} and aggregates results for a summary report. + * + *
This tracker maintains: + * + *
+ * Configuration Resolution + * Builder Definition Extraction + * DTO Mapping + * Code Generation + * ├─ Source Construction + * │ ├─ Element Building + * │ │ ├─ Class Creation + * │ │ ├─ Class Metadata + * │ │ ├─ Fields + * │ │ ├─ Constructors + * │ │ ├─ Methods + * │ │ ├─ Nested Types + * │ │ └─ Class Annotations + * │ ├─ String Generation + * │ └─ Formatting + * └─ File Writing + *+ * Percentages are calculated relative to the parent phase. + *
All methods are empty, so the JIT compiler can eliminate them entirely when the tracker is + * fixed at construction time. This ensures zero overhead when performance tracking is disabled. + */ +public final class NoOpPerformanceTracker implements PerformanceTracker { + + @Override + public void startPhase(String phase, String className) { + // No-op + } + + @Override + public void endPhase(String phase) { + // No-op + } + + @Override + public void startGenerator(String generatorName) { + // No-op + } + + @Override + public void endGenerator(String generatorName) { + // No-op + } + + @Override + public void startEnhancer(String enhancerName) { + // No-op + } + + @Override + public void endEnhancer(String enhancerName) { + // No-op + } + + @Override + public void startClass(String className) { + // No-op + } + + @Override + public void endClass(int fieldCount, int collectionCount) { + // No-op + } + + @Override + public void generateReport(ProcessingLogger logger) { + // No-op + } +} diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/processing/logging/PerformanceTracker.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/processing/logging/PerformanceTracker.java new file mode 100644 index 00000000..7d154778 --- /dev/null +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/processing/logging/PerformanceTracker.java @@ -0,0 +1,135 @@ +/* + * MIT License + * + * Copyright (c) 2026 Andreas Igel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.javahelpers.simple.builders.processor.processing.logging; + +/** + * Interface for tracking performance metrics during annotation processing. + * + *
Implementations: + * + *
The No-Op pattern ensures that when performance tracking is disabled, the JIT compiler can + * eliminate all tracking calls entirely, as the receiver type is fixed at construction time and all + * methods are empty. + */ +public interface PerformanceTracker { + + // Top-level phases + String PHASE_CONFIGURATION_RESOLUTION = "Configuration Resolution"; + String PHASE_BUILDER_DEFINITION_EXTRACTION = "Builder Definition Extraction"; + String PHASE_DTO_MAPPING = "DTO Mapping"; + String PHASE_CODE_GENERATION = "Code Generation"; + + // Code Generation children + String PHASE_SOURCE_CONSTRUCTION = "Source Construction"; + String PHASE_FILE_WRITING = "File Writing"; + + // Source Construction children + String PHASE_ELEMENT_BUILDING = "Element Building"; + String PHASE_STRING_GENERATION = "String Generation"; + String PHASE_FORMATTING = "Formatting"; + + // Element Building children + String PHASE_CLASS_CREATION = "Class Creation"; + String PHASE_CLASS_METADATA = "Class Metadata"; + String PHASE_FIELDS = "Fields"; + String PHASE_CONSTRUCTORS = "Constructors"; + String PHASE_METHODS = "Methods"; + String PHASE_NESTED_TYPES = "Nested Types"; + String PHASE_CLASS_ANNOTATIONS = "Class Annotations"; + + /** + * Starts tracking a processing phase for a specific class. + * + * @param phase the phase identifier (e.g., "Configuration Resolution", "Builder Definition + * Extraction") + * @param className the simple name of the class being processed + */ + void startPhase(String phase, String className); + + /** + * Ends tracking a processing phase. + * + * @param phase the phase identifier that was started + */ + void endPhase(String phase); + + /** + * Starts tracking an individual method generator invocation. + * + * @param generatorName the simple class name of the method generator + */ + void startGenerator(String generatorName); + + /** + * Ends tracking an individual method generator invocation. + * + * @param generatorName the simple class name of the method generator that was started + */ + void endGenerator(String generatorName); + + /** + * Starts tracking an individual builder enhancer invocation. + * + * @param enhancerName the simple class name of the builder enhancer + */ + void startEnhancer(String enhancerName); + + /** + * Ends tracking an individual builder enhancer invocation. + * + * @param enhancerName the simple class name of the builder enhancer that was started + */ + void endEnhancer(String enhancerName); + + /** + * Records the start of processing for a specific class. + * + *
Call this before any work begins for the class. Field and collection counts are not yet + * known at this point; they are passed to {@link #endClass(int, int)} after extraction. + * + * @param className the simple name of the class being processed + */ + void startClass(String className); + + /** + * Records the end of processing for the current class. + * + * @param fieldCount the number of fields in the class + * @param collectionCount the number of collection-type fields + */ + void endClass(int fieldCount, int collectionCount); + + /** + * Generates and logs the performance report. + * + * @param logger the processing logger to output the report + */ + void generateReport(ProcessingLogger logger); +} diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/processing/ProcessingLogger.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/processing/logging/ProcessingLogger.java similarity index 98% rename from processor/src/main/java/org/javahelpers/simple/builders/processor/processing/ProcessingLogger.java rename to processor/src/main/java/org/javahelpers/simple/builders/processor/processing/logging/ProcessingLogger.java index 5be6032e..fa3bf1a5 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/processing/ProcessingLogger.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/processing/logging/ProcessingLogger.java @@ -22,12 +22,14 @@ * SOFTWARE. */ -package org.javahelpers.simple.builders.processor.processing; +package org.javahelpers.simple.builders.processor.processing.logging; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.tools.Diagnostic; +import org.javahelpers.simple.builders.processor.processing.CompilerArgumentsEnum; +import org.javahelpers.simple.builders.processor.processing.CompilerArgumentsReader; /** * Logger for all messages during annotation processing. Providing util-functions for posting diff --git a/processor/src/test/java/org/javahelpers/simple/builders/processor/RoasterCodeGeneratorResilienceTest.java b/processor/src/test/java/org/javahelpers/simple/builders/processor/RoasterCodeGeneratorResilienceTest.java index 5a01ba85..cd9caa34 100644 --- a/processor/src/test/java/org/javahelpers/simple/builders/processor/RoasterCodeGeneratorResilienceTest.java +++ b/processor/src/test/java/org/javahelpers/simple/builders/processor/RoasterCodeGeneratorResilienceTest.java @@ -49,7 +49,8 @@ import org.javahelpers.simple.builders.processor.model.method.MethodCodeDto; import org.javahelpers.simple.builders.processor.model.method.MethodCodePlaceholder; import org.javahelpers.simple.builders.processor.model.type.TypeName; -import org.javahelpers.simple.builders.processor.processing.ProcessingLogger; +import org.javahelpers.simple.builders.processor.processing.logging.NoOpPerformanceTracker; +import org.javahelpers.simple.builders.processor.processing.logging.ProcessingLogger; import org.junit.jupiter.api.Test; /** @@ -85,7 +86,8 @@ void shouldWrapRenderingRuntimeExceptionInBuilderException() { classDef.addConstructor(constructor); ProcessingEnvironment env = new NoopProcessingEnvironment(); - RoasterCodeGenerator generator = new RoasterCodeGenerator(env, new ProcessingLogger(env)); + RoasterCodeGenerator generator = + new RoasterCodeGenerator(env, new ProcessingLogger(env), new NoOpPerformanceTracker()); BuilderException thrown = assertThrows(BuilderException.class, () -> generator.generateClass(classDef));