@@ -609,6 +609,8 @@ protected void _addCreators(Map<String, POJOPropertyBuilder> props)
609
609
// Next: remove creators marked as explicitly disabled
610
610
_removeDisabledCreators (constructors );
611
611
_removeDisabledCreators (factories );
612
+ // And then remove non-annotated static methods that do not look like factories
613
+ _removeNonFactoryStaticMethods (factories );
612
614
613
615
// and use annotations to find explicitly chosen Creators
614
616
if (_useAnnotations ) { // can't have explicit ones without Annotation introspection
@@ -630,13 +632,37 @@ protected void _addCreators(Map<String, POJOPropertyBuilder> props)
630
632
// (JDK 17 Record/Scala/Kotlin)
631
633
if (!creators .hasPropertiesBased ()) {
632
634
// for Records:
633
- if ((canonical != null ) && constructors .contains (canonical )) {
634
- constructors .remove (canonical );
635
- creators .setPropertiesBased (_config , canonical , "canonical" );
635
+ if (canonical != null ) {
636
+ // ... but only process if still included as a candidate
637
+ if (constructors .remove (canonical )) {
638
+ // But wait! Could be delegating
639
+ if (_isDelegatingConstructor (canonical )) {
640
+ creators .addExplicitDelegating (canonical );
641
+ } else {
642
+ creators .setPropertiesBased (_config , canonical , "canonical" );
643
+ }
644
+ }
645
+ }
646
+ }
647
+
648
+ // One more thing: if neither explicit (constructor or factory) nor
649
+ // canonical (constructor?), consider implicit Constructor with
650
+ // all named.
651
+ final ConstructorDetector ctorDetector = _config .getConstructorDetector ();
652
+ if (!creators .hasPropertiesBasedOrDelegating ()
653
+ && !ctorDetector .requireCtorAnnotation ()) {
654
+ // But only if no default constructor available OR if we are configured
655
+ // to prefer properties-based Creators
656
+ if ((_classDef .getDefaultConstructor () == null )
657
+ || ctorDetector .singleArgCreatorDefaultsToProperties ()) {
658
+ _addImplicitConstructor (creators , constructors , props );
636
659
}
637
660
}
638
661
639
662
// Anything else left, add as possible implicit Creators
663
+ // ... but first, trim non-visible
664
+ _removeNonVisibleCreators (constructors );
665
+ _removeNonVisibleCreators (factories );
640
666
creators .setImplicitDelegating (constructors , factories );
641
667
642
668
// And finally add logical properties for the One Properties-based
@@ -650,6 +676,20 @@ protected void _addCreators(Map<String, POJOPropertyBuilder> props)
650
676
}
651
677
}
652
678
679
+ // Method to determine if given non-explictly-annotated constructor
680
+ // looks like delegating one
681
+ private boolean _isDelegatingConstructor (PotentialCreator ctor )
682
+ {
683
+ // Only consider single-arg case, for now
684
+ if (ctor .paramCount () == 1 ) {
685
+ // Main thing: @JsonValue makes it delegating:
686
+ if ((_jsonValueAccessors != null ) && !_jsonValueAccessors .isEmpty ()) {
687
+ return true ;
688
+ }
689
+ }
690
+ return false ;
691
+ }
692
+
653
693
private List <PotentialCreator > _collectCreators (List <? extends AnnotatedWithParams > ctors )
654
694
{
655
695
if (ctors .isEmpty ()) {
@@ -675,6 +715,49 @@ private void _removeDisabledCreators(List<PotentialCreator> ctors)
675
715
}
676
716
}
677
717
718
+ private void _removeNonVisibleCreators (List <PotentialCreator > ctors )
719
+ {
720
+ Iterator <PotentialCreator > it = ctors .iterator ();
721
+ while (it .hasNext ()) {
722
+ PotentialCreator ctor = it .next ();
723
+ boolean visible = (ctor .paramCount () == 1 )
724
+ ? _visibilityChecker .isScalarConstructorVisible (ctor .creator ())
725
+ : _visibilityChecker .isCreatorVisible (ctor .creator ());
726
+ if (!visible ) {
727
+ it .remove ();
728
+ }
729
+ }
730
+ }
731
+
732
+ private void _removeNonFactoryStaticMethods (List <PotentialCreator > ctors )
733
+ {
734
+ final Class <?> rawType = _type .getRawClass ();
735
+ Iterator <PotentialCreator > it = ctors .iterator ();
736
+ while (it .hasNext ()) {
737
+ // explicit mode? Retain (for now)
738
+ PotentialCreator ctor = it .next ();
739
+ if (ctor .creatorMode () != null ) {
740
+ continue ;
741
+ }
742
+ // Copied from `BasicBeanDescription.isFactoryMethod()`
743
+ AnnotatedWithParams factory = ctor .creator ();
744
+ if (rawType .isAssignableFrom (factory .getRawType ())
745
+ && ctor .paramCount () == 1 ) {
746
+ String name = factory .getName ();
747
+
748
+ if ("valueOf" .equals (name )) {
749
+ continue ;
750
+ } else if ("fromString" .equals (name )) {
751
+ Class <?> cls = factory .getRawParameterType (0 );
752
+ if (cls == String .class || CharSequence .class .isAssignableFrom (cls )) {
753
+ continue ;
754
+ }
755
+ }
756
+ }
757
+ it .remove ();
758
+ }
759
+ }
760
+
678
761
private void _addExplicitlyAnnotatedCreators (PotentialCreators collector ,
679
762
List <PotentialCreator > ctors ,
680
763
Map <String , POJOPropertyBuilder > props ,
@@ -690,48 +773,25 @@ private void _addExplicitlyAnnotatedCreators(PotentialCreators collector,
690
773
if (ctor .creatorMode () == null ) {
691
774
continue ;
692
775
}
776
+
693
777
it .remove ();
694
778
695
- Boolean propsBased = null ;
696
-
779
+ boolean isPropsBased ;
780
+
697
781
switch (ctor .creatorMode ()) {
698
782
case DELEGATING :
699
- propsBased = false ;
783
+ isPropsBased = false ;
700
784
break ;
701
785
case PROPERTIES :
702
- propsBased = true ;
786
+ isPropsBased = true ;
703
787
break ;
704
788
case DEFAULT :
705
789
default :
706
- // First things first: if not single-arg Creator, must be Properties-based
707
- // !!! Or does it? What if there's @JacksonInject etc?
708
- if (ctor .paramCount () != 1 ) {
709
- propsBased = true ;
710
- }
790
+ isPropsBased = _isExplicitlyAnnotatedCreatorPropsBased (ctor ,
791
+ props , ctorDetector );
711
792
}
712
793
713
- // Must be 1-arg case:
714
- if (propsBased == null ) {
715
- // Is ambiguity/heuristics allowed?
716
- if (ctorDetector .requireCtorAnnotation ()) {
717
- throw new IllegalArgumentException (String .format (
718
- "Ambiguous 1-argument Creator; `ConstructorDetector` requires specifying `mode`: %s" ,
719
- ctor ));
720
- }
721
-
722
- // First things first: if explicit names found, is Properties-based
723
- ctor .introspectParamNames (_config );
724
- propsBased = ctor .hasExplicitNames ()
725
- || ctorDetector .singleArgCreatorDefaultsToProperties ();
726
- // One more possibility: implicit name that maps to implied
727
- // property based on Field/Getter/Setter
728
- if (!propsBased ) {
729
- String implName = ctor .implicitNameSimple (0 );
730
- propsBased = (implName != null ) && props .containsKey (implName );
731
- }
732
- }
733
-
734
- if (propsBased ) {
794
+ if (isPropsBased ) {
735
795
// Skipping done if we already got higher-precedence Creator
736
796
if (!skipPropsBased ) {
737
797
collector .setPropertiesBased (_config , ctor , "explicit" );
@@ -742,6 +802,56 @@ private void _addExplicitlyAnnotatedCreators(PotentialCreators collector,
742
802
}
743
803
}
744
804
805
+ private boolean _isExplicitlyAnnotatedCreatorPropsBased (PotentialCreator ctor ,
806
+ Map <String , POJOPropertyBuilder > props , ConstructorDetector ctorDetector )
807
+ {
808
+ if (ctor .paramCount () == 1 ) {
809
+ // Is ambiguity/heuristics allowed?
810
+ switch (ctorDetector .singleArgMode ()) {
811
+ case DELEGATING :
812
+ return false ;
813
+ case PROPERTIES :
814
+ return true ;
815
+ case REQUIRE_MODE :
816
+ throw new IllegalArgumentException (String .format (
817
+ "Single-argument constructor (%s) is annotated but no 'mode' defined; `ConstructorDetector`"
818
+ + "configured with `SingleArgConstructor.REQUIRE_MODE`" ,
819
+ ctor .creator ()));
820
+ case HEURISTIC :
821
+ default :
822
+ }
823
+ }
824
+
825
+ // First: if explicit names found, is Properties-based
826
+ ctor .introspectParamNames (_config );
827
+ if (ctor .hasExplicitNames ()) {
828
+ return true ;
829
+ }
830
+ // Second: [databind#3180] @JsonValue indicates delegating
831
+ if ((_jsonValueAccessors != null ) && !_jsonValueAccessors .isEmpty ()) {
832
+ return false ;
833
+ }
834
+ if (ctor .paramCount () == 1 ) {
835
+ // One more possibility: implicit name that maps to implied
836
+ // property with at least one visible accessor
837
+ String implName = ctor .implicitNameSimple (0 );
838
+ if (implName != null ) {
839
+ POJOPropertyBuilder prop = props .get (implName );
840
+ if ((prop != null ) && prop .anyVisible () && !prop .anyIgnorals ()) {
841
+ return true ;
842
+ }
843
+ }
844
+ // Second: injectable also suffices
845
+ if ((_annotationIntrospector != null )
846
+ && _annotationIntrospector .findInjectableValue (_config , ctor .param (0 )) != null ) {
847
+ return true ;
848
+ }
849
+ return false ;
850
+ }
851
+ // Trickiest case: rely on existence of implicit names and/or injectables
852
+ return ctor .hasNameOrInjectForAllParams (_config );
853
+ }
854
+
745
855
private void _addCreatorsWithAnnotatedNames (PotentialCreators collector ,
746
856
List <PotentialCreator > ctors )
747
857
{
@@ -760,6 +870,55 @@ private void _addCreatorsWithAnnotatedNames(PotentialCreators collector,
760
870
}
761
871
}
762
872
873
+ private boolean _addImplicitConstructor (PotentialCreators collector ,
874
+ List <PotentialCreator > ctors , Map <String , POJOPropertyBuilder > props )
875
+ {
876
+ // Must have one and only one candidate
877
+ if (ctors .size () != 1 ) {
878
+ return false ;
879
+ }
880
+ final PotentialCreator ctor = ctors .get (0 );
881
+ // which needs to be visible
882
+ final boolean visible = (ctor .paramCount () == 1 )
883
+ ? _visibilityChecker .isScalarConstructorVisible (ctor .creator ())
884
+ : _visibilityChecker .isCreatorVisible (ctor .creator ());
885
+ if (!visible ) {
886
+ return false ;
887
+ }
888
+ ctor .introspectParamNames (_config );
889
+
890
+ // As usual, 1-param case is distinct
891
+ if (ctor .paramCount () != 1 ) {
892
+ if (!ctor .hasNameOrInjectForAllParams (_config )) {
893
+ return false ;
894
+ }
895
+ } else {
896
+ // First things first: if only param has Injectable, must be Props-based
897
+ if ((_annotationIntrospector != null )
898
+ && _annotationIntrospector .findInjectableValue (_config , ctor .param (0 )) != null ) {
899
+ // props-based, continue
900
+ } else {
901
+ // may have explicit preference
902
+ final ConstructorDetector ctorDetector = _config .getConstructorDetector ();
903
+ if (ctorDetector .singleArgCreatorDefaultsToDelegating ()) {
904
+ return false ;
905
+ }
906
+ // if not, prefer Properties-based if explicit preference OR
907
+ // property with same name with at least one visible accessor
908
+ if (!ctorDetector .singleArgCreatorDefaultsToProperties ()) {
909
+ POJOPropertyBuilder prop = props .get (ctor .implicitNameSimple (0 ));
910
+ if ((prop == null ) || !prop .anyVisible () || prop .anyIgnorals ()) {
911
+ return false ;
912
+ }
913
+ }
914
+ }
915
+ }
916
+
917
+ ctors .remove (0 );
918
+ collector .setPropertiesBased (_config , ctor , "implicit" );
919
+ return true ;
920
+ }
921
+
763
922
private void _addCreatorParams (Map <String , POJOPropertyBuilder > props ,
764
923
PotentialCreator ctor , List <POJOPropertyBuilder > creatorProps )
765
924
{
0 commit comments