Skip to content

Commit 7094f7d

Browse files
committed
Merge branch '2.12'
2 parents 5f4148e + c381ff7 commit 7094f7d

File tree

10 files changed

+323
-211
lines changed

10 files changed

+323
-211
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.fasterxml.jackson.databind.introspect;
2+
3+
import com.fasterxml.jackson.databind.cfg.MapperConfig;
4+
5+
/**
6+
* API for handlers used to "mangle" names of "getter" and "setter" methods to
7+
* find implicit property names.
8+
*
9+
* @since 2.12
10+
*/
11+
public abstract class AccessorNamingStrategy
12+
{
13+
/**
14+
* Method called to find whether given method would be considered an "is-getter"
15+
* method in context of type introspected.
16+
*<p>
17+
* Note that signature acceptability has already been checked (no arguments,
18+
* has return value) but NOT the specific limitation that return type should
19+
* be of boolean type -- implementation should apply latter check, if so desired
20+
* (some languages may use different criteria).
21+
*/
22+
public abstract String findNameForIsGetter(AnnotatedMethod am, String name);
23+
24+
/**
25+
* Method called to find whether given method would be considered a "regular"
26+
* getter method in context of type introspected.
27+
*<p>
28+
* Note that signature acceptability has already been checked (no arguments,
29+
* does have a return value) by caller.
30+
*<p>
31+
* Note that this method MAY be called for potential "is-getter" methods too
32+
* (before {@link #findNameForIsGetter})
33+
*/
34+
public abstract String findNameForRegularGetter(AnnotatedMethod am, String name);
35+
36+
/**
37+
* Method called to find whether given method would be considered a "mutator"
38+
* (usually setter, but for builders "with-method" or similar) in context of
39+
* type introspected.
40+
*<p>
41+
* Note that signature acceptability has already been checked (exactly one parameter)
42+
* by caller.
43+
*/
44+
public abstract String findNameForMutator(AnnotatedMethod am, String name);
45+
46+
// FunctionalInterface
47+
/**
48+
* Interface for provider (factory) for constructing {@link AccessorNamingStrategy} for given
49+
* type.
50+
*/
51+
public abstract static class Provider
52+
implements java.io.Serializable // since one configured with Mapper/MapperBuilder
53+
{
54+
private static final long serialVersionUID = 1L;
55+
56+
public abstract AccessorNamingStrategy forPOJO(MapperConfig<?> config, AnnotatedClass ac,
57+
String mutatorPrefix);
58+
59+
public abstract AccessorNamingStrategy forBuilder(MapperConfig<?> config,
60+
AnnotatedClass builderClass, AnnotatedClass targetClass, String mutatorPrefix);
61+
}
62+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package com.fasterxml.jackson.databind.introspect;
2+
3+
import com.fasterxml.jackson.databind.cfg.MapperConfig;
4+
5+
/**
6+
* Default {@link AccessorNamingStrategy} used by Jackson: to be used either as-is,
7+
* or as base-class with overrides.
8+
*/
9+
public class DefaultAccessorNamingStrategy
10+
extends AccessorNamingStrategy
11+
{
12+
protected final MapperConfig<?> _config;
13+
protected final AnnotatedClass _forClass;
14+
15+
/**
16+
* Prefix used by auto-detected mutators ("setters"): usually "set",
17+
* but differs for builder objects ("with" by default).
18+
*/
19+
protected final String _mutatorPrefix;
20+
21+
protected DefaultAccessorNamingStrategy(MapperConfig<?> config, AnnotatedClass forClass,
22+
String mutatorPrefix) {
23+
_config = config;
24+
_forClass = forClass;
25+
26+
_mutatorPrefix = mutatorPrefix;
27+
}
28+
29+
@Override
30+
public String findNameForIsGetter(AnnotatedMethod am, String name)
31+
{
32+
final Class<?> rt = am.getRawType();
33+
if (rt == Boolean.class || rt == Boolean.TYPE) {
34+
if (name.startsWith("is")) { // plus, must return a boolean
35+
return stdManglePropertyName(name, 2);
36+
}
37+
}
38+
return null;
39+
}
40+
41+
@Override
42+
public String findNameForRegularGetter(AnnotatedMethod am, String name)
43+
{
44+
if (name.startsWith("get")) {
45+
// 16-Feb-2009, tatu: To handle [JACKSON-53], need to block CGLib-provided
46+
// method "getCallbacks". Not sure of exact safe criteria to get decent
47+
// coverage without false matches; but for now let's assume there is
48+
// no reason to use any such getter from CGLib.
49+
if ("getCallbacks".equals(name)) {
50+
if (isCglibGetCallbacks(am)) {
51+
return null;
52+
}
53+
} else if ("getMetaClass".equals(name)) {
54+
// 30-Apr-2009, tatu: Need to suppress serialization of a cyclic reference
55+
if (isGroovyMetaClassGetter(am)) {
56+
return null;
57+
}
58+
}
59+
return stdManglePropertyName(name, 3);
60+
}
61+
return null;
62+
}
63+
64+
@Override
65+
public String findNameForMutator(AnnotatedMethod am, String name)
66+
{
67+
if (name.startsWith(_mutatorPrefix)) {
68+
return stdManglePropertyName(name, _mutatorPrefix.length());
69+
}
70+
return null;
71+
}
72+
73+
/*
74+
/**********************************************************************
75+
/* Name-mangling methods copied in 2.12 from "BeanUtil"
76+
/**********************************************************************
77+
*/
78+
79+
// 24-Sep-2017, tatu: note that "std" here refers to earlier (1.x, 2.x) distinction
80+
// between "legacy" (slightly non-conforming) and "std" (fully conforming): with 3.x
81+
// only latter exists.
82+
protected static String stdManglePropertyName(final String basename, final int offset)
83+
{
84+
final int end = basename.length();
85+
if (end == offset) { // empty name, nope
86+
return null;
87+
}
88+
// first: if it doesn't start with capital, return as-is
89+
char c0 = basename.charAt(offset);
90+
char c1 = Character.toLowerCase(c0);
91+
if (c0 == c1) {
92+
return basename.substring(offset);
93+
}
94+
// 17-Dec-2014, tatu: As per [databind#653], need to follow more
95+
// closely Java Beans spec; specifically, if two first are upper-case,
96+
// then no lower-casing should be done.
97+
if ((offset + 1) < end) {
98+
if (Character.isUpperCase(basename.charAt(offset+1))) {
99+
return basename.substring(offset);
100+
}
101+
}
102+
StringBuilder sb = new StringBuilder(end - offset);
103+
sb.append(c1);
104+
sb.append(basename, offset+1, end);
105+
return sb.toString();
106+
}
107+
108+
/*
109+
/**********************************************************************
110+
/* Legacy methods copied in 2.12 from "BeanUtil" -- are these still needed?
111+
/**********************************************************************
112+
*/
113+
114+
// This method was added to address the need to weed out CGLib-injected
115+
// "getCallbacks" method.
116+
// At this point caller has detected a potential getter method with
117+
// name "getCallbacks" and we need to determine if it is indeed injected
118+
// by Cglib. We do this by verifying that the result type is "net.sf.cglib.proxy.Callback[]"
119+
private static boolean isCglibGetCallbacks(AnnotatedMethod am)
120+
{
121+
Class<?> rt = am.getRawType();
122+
// Ok, first: must return an array type
123+
if (rt.isArray()) {
124+
// And that type needs to be "net.sf.cglib.proxy.Callback".
125+
// Theoretically could just be a type that implements it, but
126+
// for now let's keep things simple, fix if need be.
127+
128+
Class<?> compType = rt.getComponentType();
129+
// Actually, let's just verify it's a "net.sf.cglib.*" class/interface
130+
String className = compType.getName();
131+
if (className.contains(".cglib")) {
132+
return className.startsWith("net.sf.cglib")
133+
// also, as per [JACKSON-177]
134+
|| className.startsWith("org.hibernate.repackage.cglib")
135+
// and [core#674]
136+
|| className.startsWith("org.springframework.cglib");
137+
}
138+
}
139+
return false;
140+
}
141+
142+
// Another helper method to deal with Groovy's problematic metadata accessors
143+
private static boolean isGroovyMetaClassGetter(AnnotatedMethod am) {
144+
return am.getRawType().getName().startsWith("groovy.lang");
145+
}
146+
147+
/*
148+
/**********************************************************************
149+
/* Standard Provider implementation
150+
/**********************************************************************
151+
*/
152+
153+
/**
154+
* Provider for {@link DefaultAccessorNamingStrategy}.
155+
*/
156+
protected static class Provider
157+
extends AccessorNamingStrategy.Provider
158+
implements java.io.Serializable
159+
{
160+
private static final long serialVersionUID = 1L;
161+
162+
@Override
163+
public AccessorNamingStrategy forPOJO(MapperConfig<?> config, AnnotatedClass ac,
164+
String mutatorPrefix) {
165+
return new DefaultAccessorNamingStrategy(config, ac, mutatorPrefix);
166+
}
167+
168+
@Override
169+
public AccessorNamingStrategy forBuilder(MapperConfig<?> config, AnnotatedClass builderClass,
170+
AnnotatedClass targetClass, String mutatorPrefix)
171+
{
172+
return new DefaultAccessorNamingStrategy(config, builderClass, mutatorPrefix);
173+
}
174+
175+
}
176+
}

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

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
1212
import com.fasterxml.jackson.databind.cfg.MapperConfig;
13-
import com.fasterxml.jackson.databind.util.BeanUtil;
1413
import com.fasterxml.jackson.databind.util.ClassUtil;
1514

1615
/**
@@ -19,6 +18,10 @@
1918
*/
2019
public class POJOPropertiesCollector
2120
{
21+
// !!! TEMPORARY
22+
protected final static AccessorNamingStrategy.Provider NAMING_PROVIDER
23+
= new DefaultAccessorNamingStrategy.Provider();
24+
2225
/*
2326
/**********************************************************************
2427
/* Configuration
@@ -30,6 +33,13 @@ public class POJOPropertiesCollector
3033
*/
3134
protected final MapperConfig<?> _config;
3235

36+
/**
37+
* Handler used for name-mangling of setter, getter methods
38+
*
39+
* @since 2.12
40+
*/
41+
protected final AccessorNamingStrategy _accessorNaming;
42+
3343
/**
3444
* True if introspection is done for serialization (giving
3545
* precedence for serialization annotations), or not (false, deserialization)
@@ -52,12 +62,6 @@ public class POJOPropertiesCollector
5262

5363
protected final boolean _useAnnotations;
5464

55-
/**
56-
* Prefix used by auto-detected mutators ("setters"): usually "set",
57-
* but differs for builder objects ("with" by default).
58-
*/
59-
protected final String _mutatorPrefix;
60-
6165
/*
6266
/**********************************************************************
6367
/* Collected property information
@@ -135,7 +139,7 @@ protected POJOPropertiesCollector(MapperConfig<?> config, boolean forSerializati
135139
_forSerialization = forSerialization;
136140
_type = type;
137141
_classDef = classDef;
138-
_mutatorPrefix = (mutatorPrefix == null) ? "set" : mutatorPrefix;
142+
mutatorPrefix = (mutatorPrefix == null) ? "set" : mutatorPrefix;
139143
if (config.isAnnotationProcessingEnabled()) {
140144
_useAnnotations = true;
141145
_annotationIntrospector = _config.getAnnotationIntrospector();
@@ -145,6 +149,8 @@ protected POJOPropertiesCollector(MapperConfig<?> config, boolean forSerializati
145149
}
146150
_visibilityChecker = _config.getDefaultVisibilityChecker(type.getRawClass(),
147151
classDef);
152+
153+
_accessorNaming = NAMING_PROVIDER.forPOJO(_config, classDef, mutatorPrefix);
148154
}
149155

150156
/*
@@ -578,10 +584,10 @@ protected void _addGetterMethod(Map<String, POJOPropertyBuilder> props,
578584
if (!nameExplicit) { // no explicit name; must consider implicit
579585
implName = ai.findImplicitPropertyName(_config, m);
580586
if (implName == null) {
581-
implName = BeanUtil.okNameForRegularGetter(m, m.getName());
587+
implName = _accessorNaming.findNameForRegularGetter(m, m.getName());
582588
}
583589
if (implName == null) { // if not, must skip
584-
implName = BeanUtil.okNameForIsGetter(m, m.getName());
590+
implName = _accessorNaming.findNameForIsGetter(m, m.getName());
585591
if (implName == null) {
586592
return;
587593
}
@@ -593,7 +599,10 @@ protected void _addGetterMethod(Map<String, POJOPropertyBuilder> props,
593599
// we still need implicit name to link with other pieces
594600
implName = ai.findImplicitPropertyName(_config, m);
595601
if (implName == null) {
596-
implName = BeanUtil.okNameForGetter(m);
602+
implName = _accessorNaming.findNameForRegularGetter(m, m.getName());
603+
if (implName == null) {
604+
implName = _accessorNaming.findNameForIsGetter(m, m.getName());
605+
}
597606
}
598607
// if not regular getter name, use method name as is
599608
if (implName == null) {
@@ -622,7 +631,7 @@ protected void _addSetterMethod(Map<String, POJOPropertyBuilder> props,
622631
if (!nameExplicit) { // no explicit name; must follow naming convention
623632
implName = (ai == null) ? null : ai.findImplicitPropertyName(_config, m);
624633
if (implName == null) {
625-
implName = BeanUtil.okNameForMutator(m, _mutatorPrefix);
634+
implName = _accessorNaming.findNameForMutator(m, m.getName());
626635
}
627636
if (implName == null) { // if not, must skip
628637
return;
@@ -632,7 +641,7 @@ protected void _addSetterMethod(Map<String, POJOPropertyBuilder> props,
632641
// we still need implicit name to link with other pieces
633642
implName = (ai == null) ? null : ai.findImplicitPropertyName(_config, m);
634643
if (implName == null) {
635-
implName = BeanUtil.okNameForMutator(m, _mutatorPrefix);
644+
implName = _accessorNaming.findNameForMutator(m, m.getName());
636645
}
637646
// if not regular getter name, use method name as is
638647
if (implName == null) {

0 commit comments

Comments
 (0)