2020import io .flamingock .api .annotations .EnableFlamingock ;
2121import io .flamingock .api .annotations .Stage ;
2222import io .flamingock .core .processor .util .AnnotationFinder ;
23+ import io .flamingock .core .processor .util .PathResolver ;
24+ import io .flamingock .core .processor .util .ProjectRootDetector ;
2325import io .flamingock .internal .common .core .preview .CodePreviewChange ;
2426import io .flamingock .internal .common .core .util .LoggerPreProcessor ;
2527import io .flamingock .internal .common .core .util .Serializer ;
@@ -165,17 +167,15 @@ public FlamingockAnnotationProcessor() {
165167 public synchronized void init (ProcessingEnvironment processingEnv ) {
166168 super .init (processingEnv );
167169 logger = new LoggerPreProcessor (processingEnv );
168- logger .info ("Starting EnableFlamingock annotation processor initialization. " );
170+ logger .verbose ("Starting annotation processor initialization" );
169171 resourcesRoot = getResourcesRoot ();
170172 sourceRoots = getSourcesPathList ();
171-
172-
173- logger .info ("Initialization completed. Pipeline will be processed with @EnableFlamingock annotation." );
173+ logger .verbose ("Initialization completed" );
174174 }
175175
176176 @ Override
177177 public Set <String > getSupportedOptions () {
178- return new HashSet <>(Arrays .asList ("sources" , "resources" ));
178+ return new HashSet <>(Arrays .asList ("sources" , "resources" , "flamingock.verbose" ));
179179 }
180180
181181 @ Override
@@ -189,14 +189,14 @@ public Set<String> getSupportedAnnotationTypes() {
189189 @ Override
190190 public boolean process (Set <? extends TypeElement > annotations , RoundEnvironment roundEnv ) {
191191 if (roundEnv .processingOver ()) {
192- logger .info ("Final processing round detected - skipping execution. " );
192+ logger .verbose ("Final processing round - skipping" );
193193 return false ;
194194 }
195195 if (hasProcessed ) {
196- logger .info ("Changes already processed - skipping execution." );
197196 return true ;
198197 }
199198
199+ logger .info ("Processing pipeline configuration" );
200200 AnnotationFinder annotationFinder = new AnnotationFinder (roundEnv , logger );
201201 EnableFlamingock flamingockAnnotation = annotationFinder .getPipelineAnnotation ()
202202 .orElseThrow (() -> new RuntimeException ("@EnableFlamingock annotation is mandatory. Please annotate a class with @EnableFlamingock to configure the pipeline." ));
@@ -214,7 +214,20 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
214214 String configFile = flamingockAnnotation .configFile ();
215215 FlamingockMetadata flamingockMetadata = new FlamingockMetadata (pipeline , setup , configFile );
216216 serializer .serializeFullPipeline (flamingockMetadata );
217- logger .info ("Finished processing annotated classes and generating metadata." );
217+
218+ // Generate summary - count all changes from the final pipeline (code-based + template-based)
219+ int totalStages = pipeline .getStages ().size () + (pipeline .getSystemStage () != null ? 1 : 0 );
220+ int totalChanges = 0 ;
221+ if (pipeline .getSystemStage () != null && pipeline .getSystemStage ().getTasks () != null ) {
222+ totalChanges += pipeline .getSystemStage ().getTasks ().size ();
223+ }
224+ for (PreviewStage stage : pipeline .getStages ()) {
225+ if (stage .getTasks () != null ) {
226+ totalChanges += stage .getTasks ().size ();
227+ }
228+ }
229+ logger .info ("Generated metadata: " + totalStages + " stages, " + totalChanges + " changes" );
230+
218231 hasProcessed = true ;
219232 return true ;
220233 }
@@ -381,15 +394,22 @@ private SystemPreviewStage mapAnnotationToSystemStage(Map<String, List<CodePrevi
381394 throw new RuntimeException ("@Stage annotation with type SYSTEM requires a location. Please specify the location field." );
382395 }
383396
397+ logger .verbose ("Building stage: SystemStage" );
398+
384399 String sourcesPackage = null ;
385400 String resourcesDir = null ;
386401 Collection <CodePreviewChange > changeClasses = null ;
387402
388- if (isPackageName (location )) {
403+ if (PathResolver . isPackageName (location )) {
389404 sourcesPackage = location ;
390405 changeClasses = codedChangesByPackage .get (sourcesPackage );
406+ logger .verbose ("Sources package: " + sourcesPackage );
407+ if (changeClasses != null ) {
408+ logger .verbose ("Found " + changeClasses .size () + " code-based changes in " + sourcesPackage );
409+ }
391410 } else {
392- resourcesDir = processResourceLocation (location );
411+ resourcesDir = PathResolver .processResourceLocation (location );
412+ logger .verbose ("Resources directory: " + resourcesDir );
393413 }
394414
395415 // For system stage, use hardcoded name and description to maintain consistency
@@ -411,21 +431,28 @@ private PreviewStage mapAnnotationToStage(Map<String, List<CodePreviewChange>> c
411431 throw new RuntimeException ("@Stage annotation requires a location. Please specify the location field." );
412432 }
413433
434+ // Derive name from location if not provided
435+ String name = stageAnnotation .name ();
436+ if (name .isEmpty ()) {
437+ name = PathResolver .deriveNameFromLocation (location );
438+ }
439+
440+ logger .verbose ("Building stage: " + name );
441+
414442 String sourcesPackage = null ;
415443 String resourcesDir = null ;
416444 Collection <CodePreviewChange > changeClasses = null ;
417445
418- if (isPackageName (location )) {
446+ if (PathResolver . isPackageName (location )) {
419447 sourcesPackage = location ;
420448 changeClasses = codedChangesByPackage .get (sourcesPackage );
449+ logger .verbose ("Sources package: " + sourcesPackage );
450+ if (changeClasses != null ) {
451+ logger .verbose ("Found " + changeClasses .size () + " code-based changes in " + sourcesPackage );
452+ }
421453 } else {
422- resourcesDir = processResourceLocation (location );
423- }
424-
425- // Derive name from location if not provided
426- String name = stageAnnotation .name ();
427- if (name .isEmpty ()) {
428- name = deriveNameFromLocation (location );
454+ resourcesDir = PathResolver .processResourceLocation (location );
455+ logger .verbose ("Resources directory: " + resourcesDir );
429456 }
430457
431458 return PreviewStage .defaultBuilder (stageAnnotation .type ())
@@ -568,15 +595,22 @@ private SystemPreviewStage mapToSystemStage(Map<String, List<CodePreviewChange>>
568595 throw new RuntimeException ("System stage in YAML pipeline requires a 'location' field. Please specify the location where changes are found." );
569596 }
570597
598+ logger .verbose ("Building stage: SystemStage" );
599+
571600 String sourcesPackage = null ;
572601 String resourcesDir = null ;
573602 Collection <CodePreviewChange > changeClasses = null ;
574603
575- if (isPackageName (location )) {
604+ if (PathResolver . isPackageName (location )) {
576605 sourcesPackage = location ;
577606 changeClasses = codedChangesByPackage .get (sourcesPackage );
607+ logger .verbose ("Sources package: " + sourcesPackage );
608+ if (changeClasses != null ) {
609+ logger .verbose ("Found " + changeClasses .size () + " code-based changes in " + sourcesPackage );
610+ }
578611 } else {
579- resourcesDir = processResourceLocation (location );
612+ resourcesDir = PathResolver .processResourceLocation (location );
613+ logger .verbose ("Resources directory: " + resourcesDir );
580614 }
581615
582616 // For system stage, use hardcoded name and description to maintain consistency
@@ -599,20 +633,27 @@ private PreviewStage mapToStage(Map<String, List<CodePreviewChange>> codedChange
599633 throw new RuntimeException ("Stage in YAML pipeline requires a 'location' field. Please specify the location where changes are found." );
600634 }
601635
636+ String name = stageMap .get ("name" );
637+ if (name == null || name .trim ().isEmpty ()) {
638+ name = PathResolver .deriveNameFromLocation (location );
639+ }
640+
641+ logger .verbose ("Building stage: " + name );
642+
602643 String sourcesPackage = null ;
603644 String resourcesDir = null ;
604645 Collection <CodePreviewChange > changeClasses = null ;
605646
606- if (isPackageName (location )) {
647+ if (PathResolver . isPackageName (location )) {
607648 sourcesPackage = location ;
608649 changeClasses = codedChangesByPackage .get (sourcesPackage );
650+ logger .verbose ("Sources package: " + sourcesPackage );
651+ if (changeClasses != null ) {
652+ logger .verbose ("Found " + changeClasses .size () + " code-based changes in " + sourcesPackage );
653+ }
609654 } else {
610- resourcesDir = processResourceLocation (location );
611- }
612-
613- String name = stageMap .get ("name" );
614- if (name == null || name .trim ().isEmpty ()) {
615- name = deriveNameFromLocation (location );
655+ resourcesDir = PathResolver .processResourceLocation (location );
656+ logger .verbose ("Resources directory: " + resourcesDir );
616657 }
617658
618659 return PreviewStage .defaultBuilder (StageType .from (stageMap .get ("type" )))
@@ -628,91 +669,42 @@ private PreviewStage mapToStage(Map<String, List<CodePreviewChange>> codedChange
628669
629670 @ NotNull
630671 private List <String > getSourcesPathList () {
672+ // Priority 1: Use explicitly provided parameter
631673 if (processingEnv .getOptions ().containsKey (SOURCES_PATH_ARG )) {
632674 String sourcesPath = processingEnv .getOptions ().get (SOURCES_PATH_ARG );
633- logger .info ( "'" + SOURCES_PATH_ARG + "' parameter passed: ' " + sourcesPath + "'" );
675+ logger .verbose ( "Using explicitly provided sources path: " + sourcesPath );
634676 return Collections .singletonList (sourcesPath );
635- } else {
636- logger .warn ("'" + SOURCES_PATH_ARG + "' parameter NOT passed. Searching in: '" + DEFAULT_SOURCE_DIRS + "'" );
637- return DEFAULT_SOURCE_DIRS ;
638677 }
678+
679+ // Priority 2: Auto-detect project root and convert to absolute paths
680+ File projectRoot = ProjectRootDetector .detectProjectRoot (processingEnv );
681+ if (projectRoot != null ) {
682+ logger .info ("Auto-detected project root: " + projectRoot .getAbsolutePath ());
683+ List <String > absolutePaths = ProjectRootDetector .toAbsoluteSourcePaths (projectRoot , DEFAULT_SOURCE_DIRS );
684+ logger .verbose ("Source paths: " + absolutePaths );
685+ return absolutePaths ;
686+ }
687+
688+ // Priority 3: Fall back to relative paths
689+ logger .warn ("Could not auto-detect project root, using relative paths (may fail in some IDEs)" );
690+ logger .verbose ("Using relative source paths: " + DEFAULT_SOURCE_DIRS );
691+ return DEFAULT_SOURCE_DIRS ;
639692 }
640693
641694 @ NotNull
642695 private String getResourcesRoot () {
643696 final String resourcesDir ;
644697 if (processingEnv .getOptions ().containsKey (RESOURCES_PATH_ARG )) {
645698 resourcesDir = processingEnv .getOptions ().get (RESOURCES_PATH_ARG );
646- logger .info ( "'" + RESOURCES_PATH_ARG + "' parameter passed: ' " + resourcesDir + "'" );
699+ logger .verbose ( "Using explicitly provided resources path: " + resourcesDir );
647700 } else {
648701 resourcesDir = DEFAULT_RESOURCES_PATH ;
649- logger .warn ( "'" + RESOURCES_PATH_ARG + "' parameter NOT passed. Using default '" + resourcesDir + "'" );
702+ logger .verbose ( "Resources root: " + resourcesDir );
650703 }
651704 return resourcesDir ;
652705 }
653706
654- /**
655- * Determines if the given location string represents a package name.
656- * A package name contains dots and no slashes (e.g., "com.example.migrations").
657- *
658- * @param location the location string to check
659- * @return true if the location is a package name, false otherwise
660- */
661- private boolean isPackageName (String location ) {
662- return location .contains ("." ) && !location .contains ("/" );
663- }
664-
665-
666- /**
667- * Derives a stage name from the location string by extracting the last segment.
668- * Examples:
669- * - "com.example.migrations" → "migrations"
670- * - "resources/db/migrations" → "migrations"
671- * - "/absolute/path/to/migrations" → "migrations"
672- *
673- * @param location the location string
674- * @return the derived name
675- */
676- private String deriveNameFromLocation (String location ) {
677-
678- // Remove "resources/" prefix if present
679- String cleanLocation = location ;
680- if (cleanLocation .startsWith ("resources/" )) {
681- cleanLocation = cleanLocation .substring ("resources/" .length ());
682- }
683-
684- // Split by either dots (for packages) or slashes (for paths)
685- String [] segments ;
686- if (cleanLocation .contains ("." ) && !cleanLocation .contains ("/" )) {
687- segments = cleanLocation .split ("\\ ." );
688- } else {
689- segments = cleanLocation .split ("/" );
690- }
691707
692- // Get the last non-empty segment
693- for (int i = segments .length - 1 ; i >= 0 ; i --) {
694- String segment = segments [i ].trim ();
695- if (!segment .isEmpty ()) {
696- return segment ;
697- }
698- }
699-
700- return location ;
701- }
702-
703- /**
704- * Processes a resource location to handle potential "resources/" prefix.
705- * Strips "resources/" prefix if present to avoid double-prefixing when
706- * concatenated with resourcesRoot ("src/main/resources").
707- *
708- * @param location the location string from user input
709- * @return processed location for use as resourcesDir
710- */
711- private String processResourceLocation (String location ) {
712- return location != null && location .startsWith ("resources/" )
713- ? location .substring ("resources/" .length ())
714- : location ;
715- }
716708
717709 private void validateConfiguration (EnableFlamingock pipelineAnnotation , boolean hasFileInAnnotation , boolean hasStagesInAnnotation ) {
718710 if (hasFileInAnnotation && hasStagesInAnnotation ) {
0 commit comments