|
8 | 8 |
|
9 | 9 | import com.fasterxml.jackson.annotation.JsonFormat;
|
10 | 10 | import com.fasterxml.jackson.databind.*;
|
| 11 | +import com.fasterxml.jackson.databind.cfg.ConstructorDetector; |
11 | 12 | import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
|
12 | 13 | import com.fasterxml.jackson.databind.cfg.MapperConfig;
|
13 | 14 | import com.fasterxml.jackson.databind.jdk14.JDK14Util;
|
@@ -88,7 +89,7 @@ public class POJOPropertiesCollector
|
88 | 89 | */
|
89 | 90 | protected LinkedHashMap<String, POJOPropertyBuilder> _properties;
|
90 | 91 |
|
91 |
| - protected LinkedList<POJOPropertyBuilder> _creatorProperties; |
| 92 | + protected List<POJOPropertyBuilder> _creatorProperties; |
92 | 93 |
|
93 | 94 | /**
|
94 | 95 | * A set of "field renamings" that have been discovered, indicating
|
@@ -614,10 +615,216 @@ protected void _addFields(Map<String, POJOPropertyBuilder> props)
|
614 | 615 | }
|
615 | 616 | }
|
616 | 617 |
|
| 618 | + // Completely rewritten in 2.18 |
| 619 | + protected void _addCreators(Map<String, POJOPropertyBuilder> props) |
| 620 | + { |
| 621 | + _creatorProperties = new ArrayList<>(); |
| 622 | + |
| 623 | + // First, resolve explicit annotations for all potential Creators |
| 624 | + // (but do NOT filter out DISABLED ones yet!) |
| 625 | + List<PotentialCreator> ctors = _collectCreators(_classDef.getConstructors()); |
| 626 | + List<PotentialCreator> factories = _collectCreators(_classDef.getFactoryMethods()); |
| 627 | + |
| 628 | + final PotentialCreator canonical; |
| 629 | + |
| 630 | + // Find and mark "canonical" constructor for Records. |
| 631 | + // Needs to be done early to get implicit names populated |
| 632 | + if (_isRecordType) { |
| 633 | + canonical = JDK14Util.findCanonicalRecordConstructor(_config, _classDef, ctors); |
| 634 | + } else { |
| 635 | + // !!! TODO: fetch Canonical for Kotlin, Scala, via AnnotationIntrospector? |
| 636 | + canonical = null; |
| 637 | + } |
| 638 | + |
| 639 | + // Next: remove creators marked as explicitly disabled |
| 640 | + _removeDisabledCreators(ctors); |
| 641 | + _removeDisabledCreators(factories); |
| 642 | + |
| 643 | + final PotentialCreators collector = new PotentialCreators(ctors, factories); |
| 644 | + // and use annotations to find explicitly chosen Creators |
| 645 | + if (_useAnnotations) { // can't have explicit ones without Annotation introspection |
| 646 | + // Start with Constructors as they have higher precedence: |
| 647 | + _addExplicitlyAnnotatedCreators(collector, collector.constructors, props, false); |
| 648 | + // followed by Factory methods (lower precedence) |
| 649 | + _addExplicitlyAnnotatedCreators(collector, collector.factories, props, |
| 650 | + collector.hasPropertiesBased()); |
| 651 | + } |
| 652 | + |
| 653 | + // If no Explicitly annotated creators found, look |
| 654 | + // for ones with explicitly-named ({@code @JsonProperty}) parameters |
| 655 | + if (!collector.hasPropertiesBased()) { |
| 656 | + // only discover constructor Creators? |
| 657 | + _addCreatorsWithAnnotatedNames(collector, collector.constructors); |
| 658 | + } |
| 659 | + |
| 660 | + // But if no annotation-based Creators found, find/use canonical Creator |
| 661 | + // (JDK 17 Record/Scala/Kotlin) |
| 662 | + if (!collector.hasPropertiesBased()) { |
| 663 | + // for Records: |
| 664 | + if ((canonical != null) && ctors.contains(canonical)) { |
| 665 | + ctors.remove(canonical); |
| 666 | + collector.addPropertiesBased(_config, canonical, "canonical"); |
| 667 | + } |
| 668 | + } |
| 669 | + |
| 670 | + // And finally add logical properties: |
| 671 | + PotentialCreator primary = collector.propertiesBased; |
| 672 | + if (primary != null) { |
| 673 | + _addCreatorParams(props, primary); |
| 674 | + } |
| 675 | + } |
| 676 | + |
| 677 | + private List<PotentialCreator> _collectCreators(List<? extends AnnotatedWithParams> ctors) |
| 678 | + { |
| 679 | + if (ctors.isEmpty()) { |
| 680 | + return Collections.emptyList(); |
| 681 | + } |
| 682 | + List<PotentialCreator> result = new ArrayList<>(); |
| 683 | + for (AnnotatedWithParams ctor : ctors) { |
| 684 | + JsonCreator.Mode creatorMode = _useAnnotations |
| 685 | + ? _annotationIntrospector.findCreatorAnnotation(_config, ctor) : null; |
| 686 | + result.add(new PotentialCreator(ctor, creatorMode)); |
| 687 | + } |
| 688 | + return (result == null) ? Collections.emptyList() : result; |
| 689 | + } |
| 690 | + |
| 691 | + private void _removeDisabledCreators(List<PotentialCreator> ctors) |
| 692 | + { |
| 693 | + Iterator<PotentialCreator> it = ctors.iterator(); |
| 694 | + while (it.hasNext()) { |
| 695 | + // explicitly prevented? Remove |
| 696 | + if (it.next().creatorMode == JsonCreator.Mode.DISABLED) { |
| 697 | + it.remove(); |
| 698 | + } |
| 699 | + } |
| 700 | + } |
| 701 | + |
| 702 | + private void _addExplicitlyAnnotatedCreators(PotentialCreators collector, |
| 703 | + List<PotentialCreator> ctors, |
| 704 | + Map<String, POJOPropertyBuilder> props, |
| 705 | + boolean skipPropsBased) |
| 706 | + { |
| 707 | + final ConstructorDetector ctorDetector = _config.getConstructorDetector(); |
| 708 | + Iterator<PotentialCreator> it = ctors.iterator(); |
| 709 | + while (it.hasNext()) { |
| 710 | + PotentialCreator ctor = it.next(); |
| 711 | + |
| 712 | + // If no explicit annotation, skip for now (may be discovered |
| 713 | + // at a later point) |
| 714 | + if (ctor.creatorMode == null) { |
| 715 | + continue; |
| 716 | + } |
| 717 | + it.remove(); |
| 718 | + |
| 719 | + Boolean propsBased = null; |
| 720 | + |
| 721 | + switch (ctor.creatorMode) { |
| 722 | + case DELEGATING: |
| 723 | + propsBased = false; |
| 724 | + break; |
| 725 | + case PROPERTIES: |
| 726 | + propsBased = true; |
| 727 | + break; |
| 728 | + case DEFAULT: |
| 729 | + default: |
| 730 | + // First things first: if not single-arg Creator, must be Properties-based |
| 731 | + // !!! Or does it? What if there's @JacksonInject etc? |
| 732 | + if (ctor.paramCount() != 1) { |
| 733 | + propsBased = true; |
| 734 | + } |
| 735 | + } |
| 736 | + |
| 737 | + // Must be 1-arg case: |
| 738 | + if (propsBased == null) { |
| 739 | + // Is ambiguity/heuristics allowed? |
| 740 | + if (ctorDetector.requireCtorAnnotation()) { |
| 741 | + throw new IllegalArgumentException(String.format( |
| 742 | + "Ambiguous 1-argument Creator; `ConstructorDetector` requires specifying `mode`: %s", |
| 743 | + ctor)); |
| 744 | + } |
| 745 | + |
| 746 | + // First things first: if explicit names found, is Properties-based |
| 747 | + ctor.introspectParamNames(_config); |
| 748 | + propsBased = ctor.hasExplicitNames() |
| 749 | + || ctorDetector.singleArgCreatorDefaultsToProperties(); |
| 750 | + // One more possibility: implicit name that maps to implied |
| 751 | + // property based on Field/Getter/Setter |
| 752 | + if (!propsBased) { |
| 753 | + String implName = ctor.implicitNameSimple(0); |
| 754 | + propsBased = (implName != null) && props.containsKey(implName); |
| 755 | + } |
| 756 | + } |
| 757 | + |
| 758 | + if (propsBased) { |
| 759 | + // Skipping done if we already got higher-precedence Creator |
| 760 | + if (!skipPropsBased) { |
| 761 | + collector.addPropertiesBased(_config, ctor, "explicit"); |
| 762 | + } |
| 763 | + } else { |
| 764 | + collector.addDelegating(ctor); |
| 765 | + } |
| 766 | + } |
| 767 | + } |
| 768 | + |
| 769 | + private void _addCreatorsWithAnnotatedNames(PotentialCreators collector, |
| 770 | + List<PotentialCreator> ctors) |
| 771 | + { |
| 772 | + Iterator<PotentialCreator> it = ctors.iterator(); |
| 773 | + while (it.hasNext()) { |
| 774 | + PotentialCreator ctor = it.next(); |
| 775 | + |
| 776 | + // Ok: existence of explicit (annotated) names infers properties-based: |
| 777 | + ctor.introspectParamNames(_config); |
| 778 | + if (!ctor.hasExplicitNames()) { |
| 779 | + continue; |
| 780 | + } |
| 781 | + it.remove(); |
| 782 | + |
| 783 | + collector.addPropertiesBased(_config, ctor, "implicit"); |
| 784 | + } |
| 785 | + } |
| 786 | + |
| 787 | + private void _addCreatorParams(Map<String, POJOPropertyBuilder> props, |
| 788 | + PotentialCreator ctor) |
| 789 | + { |
| 790 | + for (int i = 0, len = ctor.paramCount(); i < len; ++i) { |
| 791 | + final AnnotatedParameter param = ctor.param(i); |
| 792 | + final PropertyName explName = ctor.explicitName(i); |
| 793 | + PropertyName implName = ctor.implicitName(i); |
| 794 | + final boolean hasExplicit = (explName != null); |
| 795 | + |
| 796 | + if (!hasExplicit) { |
| 797 | + if (implName == null) { |
| 798 | + // Important: if neither implicit nor explicit name, cannot make use of |
| 799 | + // this creator parameter -- may or may not be a problem, verified at a later point. |
| 800 | + continue; |
| 801 | + } |
| 802 | + } |
| 803 | + |
| 804 | + // 27-Dec-2019, tatu: [databind#2527] may need to rename according to field |
| 805 | + if (implName != null) { |
| 806 | + String n = _checkRenameByField(implName.getSimpleName()); |
| 807 | + implName = PropertyName.construct(n); |
| 808 | + } |
| 809 | + |
| 810 | + POJOPropertyBuilder prop = (implName == null) |
| 811 | + ? _property(props, explName) : _property(props, implName); |
| 812 | + prop.addCtor(param, hasExplicit ? explName : implName, hasExplicit, true, false); |
| 813 | + _creatorProperties.add(prop); |
| 814 | + } |
| 815 | + } |
| 816 | + |
| 817 | + /* |
| 818 | + /********************************************************************** |
| 819 | + /* Deprecated (in 2.18) creator detection |
| 820 | + /********************************************************************** |
| 821 | + */ |
| 822 | + |
617 | 823 | /**
|
618 | 824 | * Method for collecting basic information on constructor(s) found
|
619 | 825 | */
|
620 |
| - protected void _addCreators(Map<String, POJOPropertyBuilder> props) |
| 826 | + @Deprecated |
| 827 | + protected void _addCreatorsOLD(Map<String, POJOPropertyBuilder> props) |
621 | 828 | {
|
622 | 829 | // can be null if annotation processing is disabled...
|
623 | 830 | if (_useAnnotations) {
|
@@ -734,8 +941,14 @@ private void _addCreatorParam(Map<String, POJOPropertyBuilder> props,
|
734 | 941 | _creatorProperties.add(prop);
|
735 | 942 | }
|
736 | 943 |
|
| 944 | + /* |
| 945 | + /********************************************************************** |
| 946 | + /* Method (getter, setter etc) introspection |
| 947 | + /********************************************************************** |
| 948 | + */ |
| 949 | + |
737 | 950 | /**
|
738 |
| - * Method for collecting basic information on all fields found |
| 951 | + * Method for collecting basic information on all accessor methods found |
739 | 952 | */
|
740 | 953 | protected void _addMethods(Map<String, POJOPropertyBuilder> props)
|
741 | 954 | {
|
|
0 commit comments