2929import com .squareup .javapoet .NameAllocator ;
3030import com .squareup .javapoet .TypeName ;
3131import com .squareup .javapoet .TypeSpec ;
32+ import com .squareup .javapoet .TypeVariableName ;
33+ import org .intellij .lang .annotations .PrintFormat ;
3234import org .jetbrains .annotations .NotNull ;
3335import org .jetbrains .annotations .Nullable ;
3436import org .neo4j .gds .annotation .Configuration ;
@@ -108,7 +110,7 @@ private TypeSpec process(Spec config, String packageName, String generatedClassN
108110 builder .addFields (fieldDefinitions .fields ());
109111
110112 String configParameterName = fieldDefinitions .names ().newName (CONFIG_VAR , CONFIG_VAR );
111- List <MemberDefinition > implMembers = config .members ().stream ()
113+ List <MemberDefinition > implMembers = config .members ().stream ()
112114 .flatMap (parserMember -> memberDefinition (fieldDefinitions .names (), parserMember ).stream ())
113115 .collect (Collectors .toList ());
114116
@@ -351,7 +353,7 @@ private void addValidationCode(MemberDefinition definition, CodeBlock.Builder fi
351353 definition .fieldName (),
352354 validatorCode
353355 );
354- } else if (isTypeOf (List .class , definition .fieldType ())){
356+ } else if (isTypeOf (List .class , definition .fieldType ())) {
355357 validationConsumer = validatorCode -> fieldCodeBuilder .addStatement (
356358 "this.$1N.forEach($1N -> $2L)" ,
357359 definition .fieldName (),
@@ -480,7 +482,7 @@ private Optional<MemberDefinition> memberDefinition(NameAllocator names, Spec.Me
480482 targetType ,
481483 asType (method .getEnclosingElement ()),
482484 converter ,
483- true
485+ false
484486 );
485487 }
486488
@@ -490,8 +492,8 @@ private Optional<MemberDefinition> memberDefinition(NameAllocator names, Spec.Me
490492 return converterError (
491493 method ,
492494 "[%s] is not a valid fully qualified method name: " +
493- "it must start with a fully qualified class name followed by a '#' " +
494- "and then the method name." ,
495+ "it must start with a fully qualified class name followed by a '#' " +
496+ "and then the method name." ,
495497 converter
496498 );
497499 }
@@ -502,13 +504,13 @@ private Optional<MemberDefinition> memberDefinition(NameAllocator names, Spec.Me
502504 return converterError (
503505 method ,
504506 "[%s] is not a valid fully qualified method name: " +
505- "The class [%s] cannot be found." ,
507+ "The class [%s] cannot be found." ,
506508 converter ,
507509 className
508510 );
509511 }
510512
511- return memberDefinition (names , member , targetType , classElement , methodName , false );
513+ return memberDefinition (names , member , targetType , classElement , methodName , true );
512514 }
513515
514516 private Optional <MemberDefinition > memberDefinition (
@@ -517,7 +519,7 @@ private Optional<MemberDefinition> memberDefinition(
517519 TypeMirror targetType ,
518520 TypeElement classElement ,
519521 CharSequence methodName ,
520- boolean scanInheritance
522+ boolean isFullyQualified
521523 ) {
522524 String converter = member .method ().getAnnotation (ConvertWith .class ).method ();
523525 List <ExecutableElement > validCandidates = new ArrayList <>();
@@ -531,44 +533,53 @@ private Optional<MemberDefinition> memberDefinition(
531533 return converterError (classElement , "Inherited interface was null, this could be a bug in the JDK." );
532534 }
533535
534- validCandidates .clear ();
535536
536537 for (ExecutableElement candidate : methodsIn (currentClass .getEnclosedElements ())) {
537538 if (!candidate .getSimpleName ().contentEquals (methodName )) {
538539 continue ;
539540 }
540541
541- int sizeBeforeValidation = invalidCandidates .size ();
542-
543- validateCandidateModifiers (targetType , invalidCandidates , candidate , candidate .getModifiers ());
544-
545- if (invalidCandidates .size () == sizeBeforeValidation ) {
542+ if (validateCandidateModifiers (
543+ targetType ,
544+ invalidCandidates ,
545+ isFullyQualified ,
546+ candidate ,
547+ candidate .getModifiers ()
548+ )) {
546549 validCandidates .add (candidate );
547550 }
548551 }
549552
550553 if (validCandidates .size () > 1 ) {
551554 for (ExecutableElement candidate : validCandidates ) {
552555 error (
553- String .format (Locale .ENGLISH ,"Method is ambiguous and a possible candidate for [%s]." , converter ),
556+ String .format (
557+ Locale .ENGLISH ,
558+ "Method is ambiguous and a possible candidate for [%s]." ,
559+ converter
560+ ),
554561 candidate
555562 );
556563 }
557564 return converterError (member .method (), "Multiple possible candidates found: %s." , validCandidates );
558565 }
559566
560567 if (validCandidates .size () == 1 ) {
561- ExecutableElement candidate = validCandidates .get (0 );
562- TypeMirror currentType = currentClass .asType ();
563- TypeMirror converterInputType = candidate .getParameters ().get (0 ).asType ();
568+ ExecutableElement candidate = validCandidates .getFirst ();
569+ var receiverType = candidate .getModifiers ().contains (Modifier .STATIC )
570+ ? TypeName .get (currentClass .asType ())
571+ : TypeVariableName .get ("this" ); // this isn't really the proper thing, but it works
572+ TypeMirror converterInputType = candidate .getParameters ().getFirst ().asType ();
573+
574+
564575 if (isTypeOf (Optional .class , targetType )) {
565576 return memberDefinition (names , member , targetType , Optional .of (converterInputType ))
566577 .map (definition -> ImmutableMemberDefinition .builder ()
567578 .from (definition )
568579 .addConverter (codeBlock -> CodeBlock .of (
569580 "$L.map($T::$N)" ,
570581 codeBlock ,
571- currentType ,
582+ receiverType ,
572583 candidate .getSimpleName ().toString ()
573584 ))
574585 .build ()
@@ -579,42 +590,48 @@ private Optional<MemberDefinition> memberDefinition(
579590 .from (d )
580591 .addConverter (c -> CodeBlock .of (
581592 "$T.$N($L)" ,
582- currentType ,
593+ receiverType ,
583594 candidate .getSimpleName ().toString (),
584595 c
585596 ))
586597 .build ()
587598 );
588599 }
589600
590- if (scanInheritance ) {
601+ if (! isFullyQualified ) {
591602 for (TypeMirror superInterface : currentClass .getInterfaces ()) {
592603 classesToSearch .addLast (asTypeElement (superInterface ));
593604 }
594605 }
595606 } while (!classesToSearch .isEmpty ());
596607
597608 for (InvalidCandidate invalidCandidate : invalidCandidates ) {
598- error (String .format (Locale .ENGLISH ,invalidCandidate .message (), invalidCandidate .args ()), invalidCandidate .element ());
609+ error (
610+ String .format (Locale .ENGLISH , invalidCandidate .message (), invalidCandidate .args ()),
611+ invalidCandidate .element ()
612+ );
599613 }
600614
601615 return converterError (
602616 member .method (),
603- "No suitable method found that matches [%s]. " +
604- "Make sure that the method is static, public , unary, not generic, " +
605- "does not declare any exception and returns [%s]." ,
617+ "No suitable method found that matches [%1$ s]. " +
618+ "Make sure that the method is %3$spublic , unary, not generic, " +
619+ "does not declare any exception and returns [%2$ s]." ,
606620 converter ,
607- targetType
621+ targetType ,
622+ isFullyQualified ? "static, " : ""
608623 );
609624 }
610625
611- private void validateCandidateModifiers (
626+ private boolean validateCandidateModifiers (
612627 TypeMirror targetType ,
613628 Collection <InvalidCandidate > invalidCandidates ,
629+ boolean requireStatic ,
614630 ExecutableElement candidate ,
615631 Set <Modifier > modifiers
616632 ) {
617- if (!modifiers .contains (Modifier .STATIC )) {
633+ int numberOfInvalids = invalidCandidates .size ();
634+ if (requireStatic && !modifiers .contains (Modifier .STATIC )) {
618635 invalidCandidates .add (InvalidCandidate .of (candidate , "Must be static" ));
619636 }
620637 if (!modifiers .contains (Modifier .PUBLIC )) {
@@ -640,6 +657,8 @@ private void validateCandidateModifiers(
640657 targetType
641658 ));
642659 }
660+
661+ return numberOfInvalids == invalidCandidates .size ();
643662 }
644663
645664 private Optional <MemberDefinition > memberDefinition (
@@ -755,7 +774,7 @@ private <T> Optional<T> error(CharSequence message, Element element) {
755774 return Optional .empty ();
756775 }
757776
758- private <T > Optional <T > converterError (Element element , String message , Object ... args ) {
777+ private <T > Optional <T > converterError (Element element , @ PrintFormat String message , Object ... args ) {
759778 messager .printMessage (
760779 Diagnostic .Kind .ERROR ,
761780 String .format (Locale .ENGLISH , message , args ),
0 commit comments