Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic impl of IterationType #3952

Merged
merged 5 commits into from
May 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Project: jackson-databind

#3928: `@JsonProperty` on constructor parameter changes default field serialization order
(contributed by @pjfanning)
#3950: Create new `JavaType` subtype `IterationType` (extending `SimpleType`)

2.15.2 (not yet released)

Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/fasterxml/jackson/databind/JavaType.java
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,18 @@ public final boolean isRecordType() {
return ClassUtil.isRecordType(_class);
}

/**
* Method that returns true if this instance is of type
* {@code IterationType}.
*
* @since 2.16
*
* @return True if this type is considered "iteration type"
*/
public boolean isIterationType() {
return false;
}

@Override
public final boolean isInterface() { return _class.isInterface(); }

Expand Down
158 changes: 155 additions & 3 deletions src/main/java/com/fasterxml/jackson/databind/type/IterationType.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,181 @@
package com.fasterxml.jackson.databind.type;

import java.util.Objects;

import com.fasterxml.jackson.databind.JavaType;

/**
* Specialized {@link SimpleType} for types that are allow iteration
* over Collection(-like) types: this includes types like
* {@link java.util.Iterator}.
* Referenced type is accessible using {@link #getContentType()}.
* Iterated (content) type is accessible using {@link #getContentType()}.
*
* @since 2.16
*/
public abstract class IterationType extends SimpleType
public class IterationType extends SimpleType
{
private static final long serialVersionUID = 1L;

protected final JavaType _iteratedType;

protected IterationType(Class<?> cls, TypeBindings bindings,
JavaType superClass, JavaType[] superInts, JavaType iteratedType,
Object valueHandler, Object typeHandler, boolean asStatic)
{
super(cls, bindings, superClass, superInts, Objects.hashCode(iteratedType),
valueHandler, typeHandler, asStatic);
_iteratedType = iteratedType;
}

/**
* Constructor used when upgrading into this type (via {@link #upgradeFrom},
* the usual way for {@link IterationType}s to come into existence.
* Sets up what is considered the "base" reference type
* Sets up what is considered the "base" iteration type
*/
protected IterationType(TypeBase base, JavaType iteratedType)
{
super(base);
_iteratedType = iteratedType;
}

/**
* Factory method that can be used to "upgrade" a basic type into iteration
* type; usually done via {@link TypeModifier}
*
* @param baseType Resolved non-iteration type (usually {@link SimpleType}) that is being upgraded
* @param iteratedType Iterated type; usually the first and only type parameter, but not necessarily
*/
public static IterationType upgradeFrom(JavaType baseType, JavaType iteratedType) {
Objects.requireNonNull(iteratedType);
// 19-Oct-2015, tatu: Not sure if and how other types could be used as base;
// will cross that bridge if and when need be
if (baseType instanceof TypeBase) {
return new IterationType((TypeBase) baseType, iteratedType);
}
throw new IllegalArgumentException("Cannot upgrade from an instance of "+baseType.getClass());
}

public static IterationType construct(Class<?> cls, TypeBindings bindings,
JavaType superClass, JavaType[] superInts, JavaType iteratedType)
{
return new IterationType(cls, bindings, superClass, superInts,
iteratedType, null, null, false);
}

@Override
public JavaType withContentType(JavaType contentType) {
if (_iteratedType == contentType) {
return this;
}
return new IterationType(_class, _bindings, _superClass, _superInterfaces,
contentType, _valueHandler, _typeHandler, _asStatic);
}

@Override
public IterationType withTypeHandler(Object h)
{
if (h == _typeHandler) {
return this;
}
return new IterationType(_class, _bindings, _superClass, _superInterfaces,
_iteratedType, _valueHandler, h, _asStatic);
}

@Override
public IterationType withContentTypeHandler(Object h)
{
if (h == _iteratedType.<Object>getTypeHandler()) {
return this;
}
return new IterationType(_class, _bindings, _superClass, _superInterfaces,
_iteratedType.withTypeHandler(h),
_valueHandler, _typeHandler, _asStatic);
}

@Override
public IterationType withValueHandler(Object h) {
if (h == _valueHandler) {
return this;
}
return new IterationType(_class, _bindings,
_superClass, _superInterfaces, _iteratedType,
h, _typeHandler,_asStatic);
}

@Override
public IterationType withContentValueHandler(Object h) {
if (h == _iteratedType.<Object>getValueHandler()) {
return this;
}
return new IterationType(_class, _bindings,
_superClass, _superInterfaces, _iteratedType.withValueHandler(h),
_valueHandler, _typeHandler, _asStatic);
}

@Override
public IterationType withStaticTyping() {
if (_asStatic) {
return this;
}
return new IterationType(_class, _bindings, _superClass, _superInterfaces,
_iteratedType.withStaticTyping(),
_valueHandler, _typeHandler, true);
}

@Override
public JavaType refine(Class<?> rawType, TypeBindings bindings,
JavaType superClass, JavaType[] superInterfaces) {
return new IterationType(rawType, _bindings,
superClass, superInterfaces, _iteratedType,
_valueHandler, _typeHandler, _asStatic);
}

@Override
protected String buildCanonicalName()
{
StringBuilder sb = new StringBuilder();
sb.append(_class.getName());
if ((_iteratedType != null) && _hasNTypeParameters(1)) {
sb.append('<');
sb.append(_iteratedType.toCanonical());
sb.append('>');
}
return sb.toString();
}

/*
/**********************************************************
/* Public API overrides
/**********************************************************
*/

@Override
public JavaType getContentType() {
return _iteratedType;
}

@Override
public boolean hasContentType() {
return true;
}

@Override
public boolean isIterationType() {
return true;
}

@Override
public StringBuilder getErasedSignature(StringBuilder sb) {
return _classSignature(_class, sb, true);
}

@Override
public StringBuilder getGenericSignature(StringBuilder sb)
{
_classSignature(_class, sb, false);
sb.append('<');
sb = _iteratedType.getGenericSignature(sb);
sb.append(">;");
return sb;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.BaseStream;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import java.lang.reflect.*;

import com.fasterxml.jackson.core.type.TypeReference;
Expand Down Expand Up @@ -1318,6 +1323,30 @@ private JavaType _referenceType(Class<?> rawClass, TypeBindings bindings,
return ReferenceType.construct(rawClass, bindings, superClass, superInterfaces, ct);
}

private JavaType _iterationType(Class<?> rawClass, TypeBindings bindings,
JavaType superClass, JavaType[] superInterfaces)
{
List<JavaType> typeParams = bindings.getTypeParameters();
// ok to have no types ("raw")
JavaType ct;
if (typeParams.isEmpty()) {
ct = _unknownType();
} else if (typeParams.size() == 1) {
ct = typeParams.get(0);
} else {
throw new IllegalArgumentException("Strange Iteration type "+rawClass.getName()+": cannot determine type parameters");
}
return _iterationType(rawClass, bindings, superClass, superInterfaces, ct);
}

private JavaType _iterationType(Class<?> rawClass, TypeBindings bindings,
JavaType superClass, JavaType[] superInterfaces,
JavaType iteratedType)
{
return IterationType.construct(rawClass, bindings, superClass, superInterfaces,
iteratedType);
}

/**
* Factory method to call when no special {@link JavaType} is needed,
* no generic parameters are passed. Default implementation may check
Expand Down Expand Up @@ -1582,9 +1611,25 @@ protected JavaType _fromWellKnownClass(ClassStack context, Class<?> rawType, Typ
return _referenceType(rawType, bindings, superClass, superInterfaces);
}
// 01-Nov-2015, tatu: As of 2.7, couple of potential `CollectionLikeType`s (like
// `Iterable`, `Iterator`), and `MapLikeType`s (`Map.Entry`) are not automatically
// `Iterable`), and `MapLikeType`s (`Map.Entry`) are not automatically
// detected, related to difficulties in propagating type upwards (Iterable, for
// example, is a weak, tag-on type). They may be detectable in future.
// 23-May-2023, tatu: As of 2.16 we do, however, recognized certain `IterationType`s.
if (rawType == Iterator.class || rawType == Stream.class) {
return _iterationType(rawType, bindings, superClass, superInterfaces);
}
if (BaseStream.class.isAssignableFrom(rawType)) {
if (DoubleStream.class.isAssignableFrom(rawType)) {
return _iterationType(rawType, bindings, superClass, superInterfaces,
constructType(Double.class));
} else if (IntStream.class.isAssignableFrom(rawType)) {
return _iterationType(rawType, bindings, superClass, superInterfaces,
constructType(Integer.class));
} else if (LongStream.class.isAssignableFrom(rawType)) {
return _iterationType(rawType, bindings, superClass, superInterfaces,
constructType(Long.class));
}
}
return null;
}

Expand Down
Loading