From 2d2c186ae2b45b42673c58f65636b0d2b3680654 Mon Sep 17 00:00:00 2001
From: Shalnark <65479699+Shounaks@users.noreply.github.com>
Date: Sat, 16 Mar 2024 17:42:48 +0530
Subject: [PATCH] Fix: merge conflicts
---
.../jr/ob/impl/BeanPropertyIntrospector.java | 273 +++++++++---------
1 file changed, 130 insertions(+), 143 deletions(-)
diff --git a/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanPropertyIntrospector.java b/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanPropertyIntrospector.java
index 4a46b302..484e29f6 100644
--- a/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanPropertyIntrospector.java
+++ b/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanPropertyIntrospector.java
@@ -1,40 +1,42 @@
package com.fasterxml.jackson.jr.ob.impl;
-import java.lang.reflect.*;
+import com.fasterxml.jackson.jr.ob.impl.POJODefinition.Prop;
+import com.fasterxml.jackson.jr.ob.impl.POJODefinition.PropBuilder;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
-import com.fasterxml.jackson.jr.ob.JSON;
-import com.fasterxml.jackson.jr.ob.impl.POJODefinition.Prop;
-import com.fasterxml.jackson.jr.ob.impl.POJODefinition.PropBuilder;
+import static com.fasterxml.jackson.jr.ob.JSON.Feature.INCLUDE_STATIC_FIELDS;
+import static com.fasterxml.jackson.jr.ob.JSON.Feature.USE_FIELD_MATCHING_GETTERS;
+import static java.lang.Character.isLowerCase;
+import static java.lang.Character.toLowerCase;
+import static java.lang.reflect.Modifier.*;
/**
* Helper class that jackson-jr uses by default to introspect POJO properties
* (represented as {@link POJODefinition}) to build general POJO readers
* (deserializers) and writers (serializers).
- *
+ *
* Note that most of the usage is via {@link ValueReaderLocator} and
* {@link ValueWriterLocator}
*
* @since 2.11
*/
-public class BeanPropertyIntrospector
-{
- protected final static Prop[] NO_PROPS = new Prop[0];
-
- private final static BeanPropertyIntrospector INSTANCE = new BeanPropertyIntrospector();
-
- public BeanPropertyIntrospector() { }
-
- public static BeanPropertyIntrospector instance() { return INSTANCE; }
+public final class BeanPropertyIntrospector {
+ private BeanPropertyIntrospector() {
+ }
- public POJODefinition pojoDefinitionForDeserialization(JSONReader r, Class> pojoType) {
- return _introspectDefinition(pojoType, false, r.features());
+ public static POJODefinition pojoDefinitionForDeserialization(JSONReader r, Class> pojoType) {
+ return introspectDefinition(pojoType, false, r.features());
}
- public POJODefinition pojoDefinitionForSerialization(JSONWriter w, Class> pojoType) {
- return _introspectDefinition(pojoType, true, w.features());
+ public static POJODefinition pojoDefinitionForSerialization(JSONWriter w, Class> pojoType) {
+ return introspectDefinition(pojoType, true, w.features());
}
/*
@@ -43,152 +45,72 @@ public POJODefinition pojoDefinitionForSerialization(JSONWriter w, Class> pojo
/**********************************************************************
*/
- private POJODefinition _introspectDefinition(Class> beanType,
- boolean forSerialization, int features)
- {
- Map propsByName = new TreeMap<>();
- _introspect(beanType, propsByName, features);
-
- final BeanConstructors constructors;
-
- if (forSerialization) {
- constructors = null;
- } else {
- constructors = new BeanConstructors(beanType);
- for (Constructor> ctor : beanType.getDeclaredConstructors()) {
- Class>[] argTypes = ctor.getParameterTypes();
- if (argTypes.length == 0) {
- constructors.addNoArgsConstructor(ctor);
- } else if (argTypes.length == 1) {
- Class> argType = argTypes[0];
- if (argType == String.class) {
- constructors.addStringConstructor(ctor);
- } else if (argType == Integer.class || argType == Integer.TYPE) {
- constructors.addIntConstructor(ctor);
- } else if (argType == Long.class || argType == Long.TYPE) {
- constructors.addLongConstructor(ctor);
- }
- }
- }
- }
-
- final int len = propsByName.size();
- Prop[] props;
- if (len == 0) {
- props = NO_PROPS;
- } else {
- props = new Prop[len];
- int i = 0;
- for (PropBuilder builder : propsByName.values()) {
- props[i++] = builder.build();
- }
- }
- return new POJODefinition(beanType, props, constructors);
- }
-
- private static void _introspect(Class> currType, Map props,
- int features)
- {
+ /**
+ * Brain Method of {@link BeanPropertyIntrospector}, used to get the list of props
+ */
+ private static void _introspect(Class> currType, Map props, int features) {
if (currType == null || currType == Object.class) {
return;
}
// First, check base type
_introspect(currType.getSuperclass(), props, features);
-
- final boolean noStatics = JSON.Feature.INCLUDE_STATIC_FIELDS.isDisabled(features);
- final boolean isFieldNameGettersEnabled = JSON.Feature.USE_FIELD_MATCHING_GETTERS.isEnabled(features);
-
+ final boolean isFieldNameGettersEnabled = USE_FIELD_MATCHING_GETTERS.isEnabled(features);
final Map fieldNameMap = isFieldNameGettersEnabled ? new HashMap<>() : null;
+ _populatePropsWithField(fieldNameMap, currType, props, features);
+ _populatePropWithGettersAndSetters(isFieldNameGettersEnabled, fieldNameMap, currType, props);
+ }
- // then public fields (since 2.8); may or may not be ultimately included
- // but at this point still possible
- for (Field f : currType.getDeclaredFields()) {
- if (fieldNameMap != null) {
- fieldNameMap.put(f.getName(), f);
- }
- if (!Modifier.isPublic(f.getModifiers()) || f.isEnumConstant() || f.isSynthetic()) {
- continue;
- }
- // Only include static members if (a) inclusion feature enabled and
- // (b) not final (cannot deserialize final fields)
- if (Modifier.isStatic(f.getModifiers()) && (noStatics || Modifier.isFinal(f.getModifiers()))) {
- continue;
- }
- _propFrom(props, f.getName()).withField(f);
- }
-
+ private static void _populatePropWithGettersAndSetters(boolean isFieldNameGettersEnabled, Map fieldMap, Class> currType, Map props) {
// then get methods from within this class
for (Method m : currType.getDeclaredMethods()) {
final int flags = m.getModifiers();
- // 13-Jun-2015, tatu: Skip synthetic, bridge methods altogether, for now
- // at least (add more complex handling only if absolutely necessary)
- if (Modifier.isStatic(flags) || m.isSynthetic() || m.isBridge() || isGroovyMetaClass(m.getReturnType())) {
+ final Class> returnType = m.getReturnType();
+
+ // 13-Jun-2015, tatu:
+ // Skip synthetic, bridge methods altogether, for now
+ // at least (add more complex handling only if absolutely necessary)
+ if (isStatic(flags) || m.isSynthetic() || m.isBridge() || !isPublic(flags) || isGroovyMetaClass(returnType)) {
continue;
}
- Class> argTypes[] = m.getParameterTypes();
- if (argTypes.length == 0) { // getter?
- // getters must be public to be used
- if (!Modifier.isPublic(flags)) {
- continue;
- }
- Class> resultType = m.getReturnType();
- if (resultType == Void.class) {
- continue;
- }
- String name = m.getName();
- if (name.startsWith("get")) {
- if (name.length() > 3) {
- name = decap(name.substring(3));
- _propFrom(props, name).withGetter(m);
- }
- } else if (name.startsWith("is")) {
- if (name.length() > 2) {
- // May or may not be used, but collect for now all the same:
- name = decap(name.substring(2));
- _propFrom(props, name).withIsGetter(m);
- }
- } else if (isFieldNameGettersEnabled) {
- // 10-Mar-2024: [jackson-jr#94]:
- // This will allow getters with field name as their getters,
- // like the ones generated by Groovy (or JDK 17 for Records).
- // If method name matches with field name, & method return
- // type matches the field type only then it can be considered a getter.
- Field field = fieldNameMap.get(name);
- if (field != null && Modifier.isPublic(m.getModifiers()) && m.getReturnType().equals(field.getType())) {
- // NOTE: do NOT decap, field name should be used as-is
- _propFrom(props, name).withGetter(m);
- }
- }
+ final Class>[] argTypes = m.getParameterTypes();
+ if (argTypes.length == 0 && returnType != Void.class) { // getter?
+ generatePropsWithGetter(m, props);
+ generatePropsWithIsGetter(m, props);
+ generatePropsWithFieldMatchingGetter(isFieldNameGettersEnabled, fieldMap, m, props);
} else if (argTypes.length == 1) { // setter?
// Non-public setters are fine if we can force access, don't yet check
// let's also not bother about return type; setters that return value are fine
- String name = m.getName();
- if (!name.startsWith("set") || name.length() == 3) {
- continue;
- }
- name = decap(name.substring(3));
- _propFrom(props, name).withSetter(m);
+ generatePropsWithSetter(m, props);
+ }
+ }
+ }
+
+ private static void _populatePropsWithField(Map fieldNameMap, Class> currType, Map props, int features) {
+ // then public fields (since 2.8); may or may not be ultimately included but at this point still possible
+ // Also, only include static members if
+ // (a) inclusion feature enabled and
+ // (b) not final (cannot deserialize final fields)
+ for (Field f : currType.getDeclaredFields()) {
+ if (fieldNameMap != null) {
+ fieldNameMap.put(f.getName(), f);
}
+ if (!isPublic(f.getModifiers()) || f.isEnumConstant() || f.isSynthetic() || (isStatic(f.getModifiers()) && (INCLUDE_STATIC_FIELDS.isDisabled(features) || isFinal(f.getModifiers())))) {
+ continue;
+ }
+ propFrom(props, f.getName()).withField(f);
}
}
- private static PropBuilder _propFrom(Map props, String name) {
+ private static PropBuilder propFrom(Map props, String name) {
return props.computeIfAbsent(name, Prop::builder);
}
- private static String decap(String name) {
- char c = name.charAt(0);
- char lowerC = Character.toLowerCase(c);
-
- if (c != lowerC) {
- // First: do NOT lower case if more than one leading upper case letters:
- if ((name.length() == 1)
- || !Character.isUpperCase(name.charAt(1))) {
- char chars[] = name.toCharArray();
- chars[0] = lowerC;
- return new String(chars);
- }
+ private static String _decap(String name) {
+ if (!isLowerCase(name.charAt(0)) && ((name.length() == 1) || !Character.isUpperCase(name.charAt(1)))) {
+ final char[] chars = name.toCharArray();
+ chars[0] = toLowerCase(name.charAt(0));
+ return new String(chars);
}
return name;
}
@@ -199,7 +121,72 @@ private static String decap(String name) {
* @implNote Groovy MetaClass have cyclic reference, and hence the class containing it should not be serialised without
* either removing that reference, or skipping over such references.
*/
- protected static boolean isGroovyMetaClass(Class> clazz) {
+ private static boolean isGroovyMetaClass(Class> clazz) {
return "groovy.lang.MetaClass".equals(clazz.getName());
}
-}
+
+ private static void generatePropsWithFieldMatchingGetter(boolean isFieldNameGettersEnabled,Map fieldNameMap, final Method m, final Map props) {
+ final String name = m.getName();
+ if (isFieldNameGettersEnabled) {
+ // 10-Mar-2024: [jackson-jr#94]:
+ // This will allow getters with field name as their getters,
+ // like the ones generated by Groovy (or JDK 17 for Records).
+ // If method name matches with field name, & method return
+ // type matches the field type only then it can be considered a getter.
+ Field field = fieldNameMap.get(name);
+ if (field != null && Modifier.isPublic(m.getModifiers()) && m.getReturnType().equals(field.getType())) {
+ // NOTE: do NOT decap, field name should be used as-is
+ propFrom(props, name).withGetter(m);
+ }
+ }
+ }
+
+ private static void generatePropsWithGetter(final Method method, final Map props) {
+ final String getterPrefix = "get";
+ final String name = method.getName();
+ if (name.startsWith(getterPrefix) && name.length() > getterPrefix.length()) {
+ propFrom(props, _decap(name.substring(getterPrefix.length()))).withGetter(method);
+ }
+ }
+
+ private static void generatePropsWithIsGetter(final Method method, final Map props) {
+ final String isGetterPrefix = "is";
+ final String name = method.getName();
+ if (name.startsWith(isGetterPrefix) && name.length() > isGetterPrefix.length()) {
+ propFrom(props, _decap(name.substring(isGetterPrefix.length()))).withIsGetter(method);
+ }
+ }
+
+ private static void generatePropsWithSetter(final Method method, final Map props) {
+ final String setterPrefix = "set";
+ final String name = method.getName();
+ if (name.startsWith(setterPrefix) && name.length() > setterPrefix.length()) {
+ propFrom(props, _decap(name.substring(setterPrefix.length()))).withSetter(method);
+ }
+ }
+
+ private static POJODefinition introspectDefinition(Class> beanType, boolean forSerialization, int features) {
+ final Map propsByName = new TreeMap<>();
+ _introspect(beanType, propsByName, features);
+
+ final BeanConstructors constructors = new BeanConstructors(beanType);
+ for (Constructor> ctor : beanType.getDeclaredConstructors()) {
+ final Class>[] argTypes = ctor.getParameterTypes();
+ if (argTypes.length == 0) {
+ constructors.addNoArgsConstructor(ctor);
+ } else if (argTypes.length == 1) {
+ final Class> argType = argTypes[0];
+ if (argType == String.class) {
+ constructors.addStringConstructor(ctor);
+ } else if (argType == Integer.class || argType == Integer.TYPE) {
+ constructors.addIntConstructor(ctor);
+ } else if (argType == Long.class || argType == Long.TYPE) {
+ constructors.addLongConstructor(ctor);
+ }
+ }
+ }
+
+ final Prop[] props = propsByName.values().stream().map(PropBuilder::build).toArray(Prop[]::new);
+ return new POJODefinition(beanType, props, forSerialization ? null : constructors);
+ }
+}
\ No newline at end of file