11package internal .nbbrd .service .provider ;
22
3+ import com .squareup .javapoet .*;
34import internal .nbbrd .service .Instantiator ;
45import internal .nbbrd .service .ProcessorTool ;
6+ import internal .nbbrd .service .ProcessorUtil ;
7+ import nbbrd .service .ServiceDefinition ;
58
69import javax .annotation .processing .ProcessingEnvironment ;
10+ import javax .lang .model .element .ElementKind ;
11+ import javax .lang .model .element .ExecutableElement ;
12+ import javax .lang .model .element .Modifier ;
713import javax .lang .model .element .TypeElement ;
14+ import javax .lang .model .type .TypeMirror ;
15+ import javax .lang .model .util .ElementFilter ;
816import javax .lang .model .util .Types ;
917import java .io .IOException ;
10- import java .util .Comparator ;
11- import java .util .List ;
12- import java .util .Map ;
18+ import java .util .*;
1319import java .util .function .Supplier ;
1420import java .util .stream .Collectors ;
1521import java .util .stream .Stream ;
@@ -24,7 +30,17 @@ public ServiceProviderGenerator(Supplier<ProcessingEnvironment> envSupplier) {
2430 }
2531
2632 public void generate (List <ProviderRef > annotationRefs ) throws IOException {
27- registerClassPath (annotationRefs , new ClassPathRegistry (getEnv ()));
33+ // Generate batch providers for enums and collect their batch service registrations
34+ List <BatchProviderRef > batchRefs = collectBatchProviderRefs (annotationRefs );
35+ List <BatchProviderRegistration > batchRegistrations = generateBatchProviders (batchRefs );
36+
37+ // Filter out enum providers that have batch providers generated
38+ List <ProviderRef > refsToRegister = filterOutEnumsWithBatchProviders (annotationRefs , batchRefs );
39+
40+ // Register in SPI files
41+ ClassPathRegistry classPath = new ClassPathRegistry (getEnv ());
42+ registerClassPath (refsToRegister , classPath );
43+ registerBatchProviders (batchRegistrations , classPath );
2844 }
2945
3046 private void registerClassPath (List <ProviderRef > annotationRefs , ClassPathRegistry classPath ) throws IOException {
@@ -52,6 +68,129 @@ private List<ProviderRef> generateDelegates(List<ProviderRef> refs) {
5268 return refs ;
5369 }
5470
71+ private List <BatchProviderRef > collectBatchProviderRefs (List <ProviderRef > refs ) {
72+ List <BatchProviderRef > result = new ArrayList <>();
73+ for (ProviderRef ref : refs ) {
74+ if (ref .getProvider ().getKind () == ElementKind .ENUM ) {
75+ getBatchProviderRef (ref ).ifPresent (result ::add );
76+ }
77+ }
78+ return result ;
79+ }
80+
81+ private List <ProviderRef > filterOutEnumsWithBatchProviders (List <ProviderRef > annotationRefs , List <BatchProviderRef > batchRefs ) {
82+ // Create a set of enum providers that have batch providers for quick lookup
83+ Set <TypeElement > enumsWithBatchProviders = batchRefs .stream ()
84+ .map (BatchProviderRef ::getEnumProvider )
85+ .collect (Collectors .toSet ());
86+
87+ // Filter out those enum providers
88+ return annotationRefs .stream ()
89+ .filter (ref -> !enumsWithBatchProviders .contains (ref .getProvider ()))
90+ .collect (Collectors .toList ());
91+ }
92+
93+ private Optional <BatchProviderRef > getBatchProviderRef (ProviderRef ref ) {
94+ ServiceDefinition annotation = ref .getService ().getAnnotation (ServiceDefinition .class );
95+ if (annotation == null ) {
96+ return Optional .empty ();
97+ }
98+
99+ TypeMirror batchType = ProcessorUtil .extractResultType (annotation ::batchType );
100+ if (batchType .toString ().equals (Void .class .getName ())) {
101+ return Optional .empty ();
102+ }
103+
104+ TypeElement batchTypeElement = getEnv ().asTypeElement (batchType );
105+ return findBatchMethod (batchTypeElement , ref .getService ())
106+ .map (method -> new BatchProviderRef (
107+ batchTypeElement ,
108+ ref .getProvider (),
109+ ref .getService (),
110+ method .getSimpleName ().toString (),
111+ method .getReturnType ()
112+ ));
113+ }
114+
115+ private Optional <ExecutableElement > findBatchMethod (TypeElement batchType , TypeElement serviceType ) {
116+ Types types = getEnv ().getTypeUtils ();
117+ return ElementFilter .methodsIn (batchType .getEnclosedElements ())
118+ .stream ()
119+ .filter (method -> method .getModifiers ().contains (Modifier .PUBLIC ))
120+ .filter (method -> !method .getModifiers ().contains (Modifier .STATIC ))
121+ .filter (method -> method .getParameters ().isEmpty ())
122+ .filter (method -> isBatchReturnType (method .getReturnType (), serviceType , types ))
123+ .findFirst ();
124+ }
125+
126+ private boolean isBatchReturnType (TypeMirror returnType , TypeElement serviceType , Types types ) {
127+ return BatchMethodHelper .resolve (returnType , serviceType , types , getEnv ().getElementUtils ()) != null ;
128+ }
129+
130+ private List <BatchProviderRegistration > generateBatchProviders (List <BatchProviderRef > batchRefs ) {
131+ List <BatchProviderRegistration > result = new ArrayList <>();
132+ for (BatchProviderRef ref : batchRefs ) {
133+ String providerClassName = generateBatchProvider (ref );
134+ result .add (new BatchProviderRegistration (ref .getBatchType (), providerClassName ));
135+ }
136+ return result ;
137+ }
138+
139+ private String generateBatchProvider (BatchProviderRef ref ) {
140+ TypeSpec batchImpl = buildBatchClass (ref );
141+ String packageName = getEnv ().getElementUtils ().getPackageOf (ref .getEnumProvider ()).getQualifiedName ().toString ();
142+ JavaFile javaFile = JavaFile .builder (packageName , batchImpl ).build ();
143+ ProcessorUtil .write (getEnv (), javaFile );
144+
145+ // Return the fully qualified class name
146+ return packageName .isEmpty ()
147+ ? ref .getEnumProvider ().getSimpleName () + "BatchProvider"
148+ : packageName + "." + ref .getEnumProvider ().getSimpleName () + "BatchProvider" ;
149+ }
150+
151+ private void registerBatchProviders (List <BatchProviderRegistration > registrations , ClassPathRegistry classPath ) throws IOException {
152+ for (BatchProviderRegistration registration : registrations ) {
153+ List <ProviderConfigurationFileLine > oldLines = classPath .readLinesByService (registration .getBatchService ());
154+ List <ProviderConfigurationFileLine > newLines = new ArrayList <>();
155+ newLines .add (ProviderConfigurationFileLine .ofProviderBinaryName (registration .getProviderClassName ()));
156+ classPath .writeLinesByService (concat (oldLines , newLines ), registration .getBatchService ());
157+ }
158+ }
159+
160+ private TypeSpec buildBatchClass (BatchProviderRef ref ) {
161+ String className = ref .getEnumProvider ().getSimpleName () + "BatchProvider" ;
162+ ClassName enumName = ClassName .get (ref .getEnumProvider ());
163+ ClassName batchName = ClassName .get (ref .getBatchType ());
164+
165+ MethodSpec method = MethodSpec .methodBuilder (ref .getBatchMethodName ())
166+ .addModifiers (Modifier .PUBLIC )
167+ .addAnnotation (Override .class )
168+ .returns (TypeName .get (ref .getBatchMethodReturnType ()))
169+ .addStatement ("return $L" , buildBatchMethodBody (enumName , ref ))
170+ .build ();
171+
172+ return TypeSpec .classBuilder (className )
173+ .addModifiers (Modifier .PUBLIC , Modifier .FINAL )
174+ .addSuperinterface (batchName )
175+ .addMethod (method )
176+ .build ();
177+ }
178+
179+ private CodeBlock buildBatchMethodBody (ClassName enumName , BatchProviderRef ref ) {
180+ BatchMethodHelper .ReturnKind kind = BatchMethodHelper .resolve (
181+ ref .getBatchMethodReturnType (), ref .getService (),
182+ getEnv ().getTypeUtils (), getEnv ().getElementUtils ()
183+ );
184+
185+ if (kind == BatchMethodHelper .ReturnKind .STREAM ) {
186+ return CodeBlock .of ("$T.stream($T.values())" , Arrays .class , enumName );
187+ } else if (kind == BatchMethodHelper .ReturnKind .ARRAY ) {
188+ return CodeBlock .of ("$T.values()" , enumName );
189+ } else {
190+ return CodeBlock .of ("$T.asList($T.values())" , Arrays .class , enumName );
191+ }
192+ }
193+
55194 private static boolean isStaticMethod (Instantiator o ) {
56195 return o .getKind () == Instantiator .Kind .STATIC_METHOD ;
57196 }
0 commit comments