Skip to content

Commit f550e22

Browse files
committed
Refactoring to start solving #3125
1 parent 6f4a608 commit f550e22

File tree

3 files changed

+97
-46
lines changed

3 files changed

+97
-46
lines changed

src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,20 @@ public Class<?>[] findViews(Annotated a)
524524
return (ann == null) ? null : ann.value();
525525
}
526526

527+
/**
528+
* Specific implementation that will use following tie-breaker on
529+
* given setter parameter types:
530+
*<ol>
531+
* <li>If either one is primitive type then either return {@code null}
532+
* (both primitives) or one that is primitive (when only primitive)
533+
* </li>
534+
* <li>If only one is of type {@code String}, return that setter
535+
* </li>
536+
* <li>Otherwise return {@code null}
537+
* </li>
538+
* </ol>
539+
* Returning {@code null} will indicate that resolution could not be done.
540+
*/
527541
@Override // since 2.7
528542
public AnnotatedMethod resolveSetterConflict(MapperConfig<?> config,
529543
AnnotatedMethod setter1, AnnotatedMethod setter2)
@@ -537,10 +551,12 @@ public AnnotatedMethod resolveSetterConflict(MapperConfig<?> config,
537551
if (!cls2.isPrimitive()) {
538552
return setter1;
539553
}
554+
// 10-May-2021, tatu: if both primitives cannot decide
555+
return null;
540556
} else if (cls2.isPrimitive()) {
541557
return setter2;
542558
}
543-
559+
544560
if (cls1 == String.class) {
545561
if (cls2 != String.class) {
546562
return setter1;
@@ -558,7 +574,7 @@ public PropertyName findRenameByField(MapperConfig<?> config,
558574
// Nothing to report, only used by modules. But define just as documentation
559575
return null;
560576
}
561-
577+
562578
/*
563579
/**********************************************************
564580
/* Annotations for Polymorphic Type handling

src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -466,58 +466,70 @@ public AnnotatedMethod getSetter()
466466
}
467467
// But if multiple, verify that they do not conflict...
468468
for (; next != null; next = next.next) {
469-
// Allow masking, i.e. do not fail if one is in super-class from the other
470-
Class<?> currClass = curr.value.getDeclaringClass();
471-
Class<?> nextClass = next.value.getDeclaringClass();
472-
if (currClass != nextClass) {
473-
if (currClass.isAssignableFrom(nextClass)) { // next is more specific
474-
curr = next;
475-
continue;
476-
}
477-
if (nextClass.isAssignableFrom(currClass)) { // current more specific
478-
continue;
479-
}
480-
}
481-
AnnotatedMethod nextM = next.value;
482-
AnnotatedMethod currM = curr.value;
483-
484-
/* 30-May-2014, tatu: Two levels of precedence:
485-
*
486-
* 1. Regular setters ("setX(...)")
487-
* 2. Implicit, possible setters ("x(...)")
488-
*/
489-
int priNext = _setterPriority(nextM);
490-
int priCurr = _setterPriority(currM);
491-
492-
if (priNext != priCurr) {
493-
if (priNext < priCurr) {
494-
curr = next;
495-
}
469+
AnnotatedMethod selected = _selectSetter(curr.value, next.value);
470+
if (selected == curr.value) {
496471
continue;
497472
}
498-
// 11-Dec-2015, tatu: As per [databind#1033] allow pluggable conflict resolution
499-
if (_annotationIntrospector != null) {
500-
AnnotatedMethod pref = _annotationIntrospector.resolveSetterConflict(_config,
501-
currM, nextM);
502-
503-
// note: should be one of nextM/currM; but no need to check
504-
if (pref == currM) {
505-
continue;
506-
}
507-
if (pref == nextM) {
508-
curr = next;
509-
continue;
510-
}
473+
if (selected == next.value) {
474+
curr = next;
475+
continue;
511476
}
512-
throw new IllegalArgumentException(String.format(
513-
"Conflicting setter definitions for property \"%s\": %s vs %s",
514-
getName(), curr.value.getFullName(), next.value.getFullName()));
477+
// 10-May-2021, tatu: unbreakable tie, for now; offline handling
478+
return _selectSetterFromMultiple(curr, next);
515479
}
480+
516481
// One more thing; to avoid having to do it again...
517482
_setters = curr.withoutNext();
518483
return curr.value;
519484
}
520485

486+
// @since 2.13
487+
protected AnnotatedMethod _selectSetter(AnnotatedMethod currM, AnnotatedMethod nextM)
488+
{
489+
// Allow masking, i.e. do not fail if one is in super-class from the other
490+
final Class<?> currClass = currM.getDeclaringClass();
491+
final Class<?> nextClass = nextM.getDeclaringClass();
492+
if (currClass != nextClass) {
493+
if (currClass.isAssignableFrom(nextClass)) { // next is more specific
494+
return nextM;
495+
}
496+
if (nextClass.isAssignableFrom(currClass)) { // current more specific
497+
return currM;
498+
}
499+
}
500+
501+
/* 30-May-2014, tatu: Two levels of precedence:
502+
*
503+
* 1. Regular setters ("setX(...)")
504+
* 2. Implicit, possible setters ("x(...)")
505+
*/
506+
// 25-Apr-2021, tatu: This is probably wrong, should not rely on
507+
// hard-coded "set" prefix here.
508+
int priNext = _setterPriority(nextM);
509+
int priCurr = _setterPriority(currM);
510+
511+
if (priNext != priCurr) {
512+
// Smaller value, higher; so, if next has higher precedence:
513+
if (priNext < priCurr) {
514+
return nextM;
515+
}
516+
// otherwise current one has, proceed
517+
return currM;
518+
}
519+
// 11-Dec-2015, tatu: As per [databind#1033] allow pluggable conflict resolution
520+
return (_annotationIntrospector == null) ? null
521+
: _annotationIntrospector.resolveSetterConflict(_config, currM, nextM);
522+
}
523+
524+
// @since 2.13
525+
protected AnnotatedMethod _selectSetterFromMultiple(Linked<AnnotatedMethod> curr,
526+
Linked<AnnotatedMethod> next)
527+
{
528+
throw new IllegalArgumentException(String.format(
529+
"Conflicting setter definitions for property \"%s\": %s vs %s",
530+
getName(), curr.value.getFullName(), next.value.getFullName()));
531+
}
532+
521533
@Override
522534
public AnnotatedField getField()
523535
{

src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,14 @@ public DuplicateGetterCreatorBean(@JsonProperty("bloop") @A boolean bloop) {}
231231
public boolean getBloop() { return true; }
232232
}
233233

234+
// [databind#3125]: As per existing (2.7+) logic we SHOULD tie-break
235+
// in favor of `String` but code up until 2.12 short-circuited early fail
236+
static class DupSetter3125Bean {
237+
public void setValue(Integer value) { }
238+
public void setValue(Boolean value) { }
239+
public void setValue(String value) { }
240+
}
241+
234242
/*
235243
/**********************************************************
236244
/* Unit tests
@@ -492,7 +500,22 @@ public void testDuplicateGettersCreator() throws Exception
492500
assertNotNull(prop._getters.next);
493501
assertTrue(prop._getters.next.value.hasAnnotation(A.class));
494502
}
495-
503+
/*
504+
// [databind#3125]
505+
public void testDuplicateSetters() throws Exception
506+
{
507+
POJOPropertiesCollector coll = collector(MAPPER, DupSetter3125Bean.class,
508+
false);
509+
final List<BeanPropertyDefinition> props = coll.getProperties();
510+
assertEquals(1, props.size());
511+
POJOPropertyBuilder prop = (POJOPropertyBuilder) props.get(0);
512+
assertEquals("value", prop.getName());
513+
// but this failed
514+
AnnotatedMethod m = prop.getSetter();
515+
assertNotNull(m);
516+
assertEquals(String.class, m.getRawParameterType(0));
517+
}
518+
*/
496519
private void _verifyProperty(BeanDescription beanDesc,
497520
boolean verifyDesc, boolean verifyIndex, String expDefaultValue)
498521
{

0 commit comments

Comments
 (0)