Skip to content

Commit

Permalink
Fix #4835: simplify Record detection, metadata access (#4839)
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder authored Dec 9, 2024
1 parent 7f30c9b commit e33f7c4
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 200 deletions.
1 change: 1 addition & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Versions: 3.x (for earlier see VERSION-2.x)
(contributed by Joo-Hyuk K)
#4818: Rename `AnnotationIntrospector.findDefaultCreator()` as `findPreferredCreator()`
#4820: Change JDK baseline for Jackson 3.0 from Java 8 to Java 17
#4835: Remove dynamic work-arounds wrt accessing `Record` definition
#4840: Increase minimum Android SDK required to 34 for Jackson 3.0
- Remove `MappingJsonFactory`
- Add context parameter for `TypeSerializer` contextualization (`forProperty()`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import tools.jackson.databind.MapperFeature;
import tools.jackson.databind.annotation.JsonPOJOBuilder;
import tools.jackson.databind.cfg.MapperConfig;
import tools.jackson.databind.jdk14.JDK14Util;
import tools.jackson.databind.util.RecordUtil;

/**
* Default {@link AccessorNamingStrategy} used by Jackson: to be used either as-is,
Expand Down Expand Up @@ -502,7 +502,7 @@ public RecordNaming(MapperConfig<?> config, AnnotatedClass forClass) {
// trickier: regular fields are ok (handled differently), but should
// we also allow getter discovery? For now let's do so
"get", "is", null);
String[] recordFieldNames = JDK14Util.getRecordFieldNames(forClass.getRawType());
String[] recordFieldNames = RecordUtil.getRecordFieldNames(forClass.getRawType());
// 01-May-2022, tatu: Due to [databind#3417] may return null when no info available
_fieldNames = recordFieldNames == null ?
Collections.emptySet() :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
import tools.jackson.databind.cfg.HandlerInstantiator;
import tools.jackson.databind.cfg.MapperConfig;
import tools.jackson.databind.deser.impl.UnwrappedPropertyHandler;
import tools.jackson.databind.jdk14.JDK14Util;
import tools.jackson.databind.util.ClassUtil;
import tools.jackson.databind.util.RecordUtil;

/**
* Helper class used for aggregating information about all possible
Expand Down Expand Up @@ -605,7 +605,7 @@ protected void _addCreators(Map<String, POJOPropertyBuilder> props)
// Needs to be done early to get implicit names populated
final PotentialCreator primaryCreator;
if (_isRecordType) {
primaryCreator = JDK14Util.findCanonicalRecordConstructor(_config, _classDef, constructors);
primaryCreator = RecordUtil.findCanonicalRecordConstructor(_config, _classDef, constructors);
} else {
// 02-Nov-2024, tatu: Alas, naming here is confusing: method properly
// should have been "findPrimaryCreator()" so as not to confused with
Expand Down
187 changes: 0 additions & 187 deletions src/main/java/tools/jackson/databind/jdk14/JDK14Util.java

This file was deleted.

6 changes: 0 additions & 6 deletions src/main/java/tools/jackson/databind/jdk14/package-info.java

This file was deleted.

5 changes: 2 additions & 3 deletions src/main/java/tools/jackson/databind/util/ClassUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,10 @@ public static boolean isBogusClass(Class<?> cls) {
}

/**
* Helper method for detecting Java14-added new {@code Record} types
* Helper method for detecting Java14-added {@code Record} types
*/
public static boolean isRecordType(Class<?> cls) {
Class<?> parent = cls.getSuperclass();
return (parent != null) && "java.lang.Record".equals(parent.getName());
return cls.isRecord();
}

public static boolean isObjectOrPrimitive(Class<?> cls) {
Expand Down
70 changes: 70 additions & 0 deletions src/main/java/tools/jackson/databind/util/RecordUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package tools.jackson.databind.util;

import java.lang.reflect.RecordComponent;
import java.util.Arrays;
import java.util.List;

import tools.jackson.databind.PropertyName;
import tools.jackson.databind.cfg.MapperConfig;
import tools.jackson.databind.introspect.AnnotatedClass;
import tools.jackson.databind.introspect.AnnotatedConstructor;
import tools.jackson.databind.introspect.PotentialCreator;

/**
* Helper class for finding so-called canonical constructor
* of Record types.
*/
public class RecordUtil
{
public static String[] getRecordFieldNames(Class<?> recordType) {
final RecordComponent[] components = recordType.getRecordComponents();
if (components == null) {
// not a record, or no reflective access on native image
return null;
}
return Arrays.stream(components).map(RecordComponent::getName).toArray(String[]::new);
}

public static PotentialCreator findCanonicalRecordConstructor(MapperConfig<?> config,
AnnotatedClass recordClass,
List<PotentialCreator> constructors)
{
final RecordComponent[] components = recordClass.getRawType().getRecordComponents();
if (components == null) {
// not a record, or no reflective access on native image
return null;
}

// And then locate the canonical constructor
final int argCount = components.length;
// One special case: zero-arg constructor not included in candidate List
if (argCount == 0) {
// Bit hacky but has to do: create new PotentialCreator let caller deal
AnnotatedConstructor defCtor = recordClass.getDefaultConstructor();
if (defCtor != null) {
return new PotentialCreator(defCtor, null);
}
}

main_loop:
for (PotentialCreator ctor : constructors) {
if (ctor.paramCount() != argCount) {
continue;
}
for (int i = 0; i < argCount; ++i) {
if (!ctor.creator().getRawParameterType(i).equals(components[i].getType())) {
continue main_loop;
}
}
// Found it! One more thing; get implicit Record field names:
final PropertyName[] implicits = new PropertyName[argCount];
for (int i = 0; i < argCount; ++i) {
implicits[i] = PropertyName.construct(components[i].getName());
}
return ctor.introspectParamNames(config, implicits);
}

throw new IllegalArgumentException("Failed to find the canonical Record constructor of type "
+ClassUtil.getTypeDescription(recordClass.getType()));
}
}

0 comments on commit e33f7c4

Please sign in to comment.