Skip to content

Commit

Permalink
Disallow null as skipPast
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcono1234 committed Aug 20, 2023
1 parent f5a27be commit cc4cc45
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 25 deletions.
29 changes: 12 additions & 17 deletions gson/src/main/java/com/google/gson/Gson.java
Original file line number Diff line number Diff line change
Expand Up @@ -642,33 +642,28 @@ public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
* types, our stats factory will not count the number of String or primitives that will be
* read or written.
*
* <p>If {@code skipPast} is {@code null} or a factory which has neither been registered
* on the {@link GsonBuilder} nor specified with the {@link JsonAdapter @JsonAdapter} annotation
* on a class, then this method behaves as if {@link #getAdapter(TypeToken)} had been called.
* This also means that for fields with {@code @JsonAdapter} annotation this method behaves
* normally like {@code getAdapter} (except for corner cases where a custom {@link InstanceCreator}
* is used to create an instance of the factory).
* <p>If {@code skipPast} is a factory which has neither been registered on the {@link GsonBuilder}
* nor specified with the {@link JsonAdapter @JsonAdapter} annotation on a class, then this
* method behaves as if {@link #getAdapter(TypeToken)} had been called. This also means that
* for fields with {@code @JsonAdapter} annotation this method behaves normally like {@code getAdapter}
* (except for corner cases where a custom {@link InstanceCreator} is used to create an
* instance of the factory).
*
* @param skipPast The type adapter factory that needs to be skipped while searching for
* a matching type adapter. In most cases, you should just pass <i>this</i> (the type adapter
* factory from where {@code getDelegateAdapter} method is being invoked). May be {@code null}.
* factory from where {@code getDelegateAdapter} method is being invoked).
* @param type Type for which the delegate adapter is being searched for.
*
* @since 2.2
*/
public <T> TypeAdapter<T> getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken<T> type) {
Objects.requireNonNull(skipPast, "skipPast must not be null");
Objects.requireNonNull(type, "type must not be null");

if (skipPast != null) {
if (jsonAdapterFactory.isClassJsonAdapterFactory(type.getRawType(), skipPast)) {
skipPast = jsonAdapterFactory;
} else if (!factories.contains(skipPast)) {
// Probably a factory from @JsonAdapter on a field
skipPast = null;
}
}

if (skipPast == null) {
if (jsonAdapterFactory.isClassJsonAdapterFactory(type.getRawType(), skipPast)) {
skipPast = jsonAdapterFactory;
} else if (!factories.contains(skipPast)) {
// Probably a factory from @JsonAdapter on a field
return getAdapter(type);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,13 @@ private static class DummyTypeAdapterFactory implements TypeAdapterFactory {
* Factory used for {@link TreeTypeAdapter}s created for {@code @JsonAdapter}
* on a class.
*/
private static final TypeAdapterFactory TREE_TYPE_DUMMY_FACTORY = new DummyTypeAdapterFactory();
private static final TypeAdapterFactory TREE_TYPE_CLASS_DUMMY_FACTORY = new DummyTypeAdapterFactory();

/**
* Factory used for {@link TreeTypeAdapter}s created for {@code @JsonAdapter}
* on a field.
*/
private static final TypeAdapterFactory TREE_TYPE_FIELD_DUMMY_FACTORY = new DummyTypeAdapterFactory();

private final ConstructorConstructor constructorConstructor;
/**
Expand Down Expand Up @@ -115,12 +121,17 @@ TypeAdapter<?> getTypeAdapter(ConstructorConstructor constructorConstructor, Gso
? (JsonDeserializer<?>) instance
: null;

TypeAdapterFactory skipPast = null;
TypeAdapterFactory skipPast;
if (isClassAnnotation) {
// Use dummy `skipPast` value; otherwise TreeTypeAdapter's call to `Gson.getDelegateAdapter` would
// cause infinite recursion because it would keep returning adapter specified by @JsonAdapter
skipPast = TREE_TYPE_DUMMY_FACTORY;
// Use dummy `skipPast` value and put it into factory map; otherwise TreeTypeAdapter's call to
// `Gson.getDelegateAdapter` would cause infinite recursion because it would keep returning the
// adapter specified by @JsonAdapter
skipPast = TREE_TYPE_CLASS_DUMMY_FACTORY;
adapterFactoryMap.put(type.getRawType(), skipPast);
} else {
// Use dummy `skipPast` value, but don't put it into factory map; this way `Gson.getDelegateAdapter`
// will return regular adapter for field type without actually skipping past any factory
skipPast = TREE_TYPE_FIELD_DUMMY_FACTORY;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
TypeAdapter<?> tempAdapter = new TreeTypeAdapter(serializer, deserializer, gson, type, skipPast, nullSafe);
Expand Down
6 changes: 3 additions & 3 deletions gson/src/test/java/com/google/gson/GsonTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -325,12 +325,12 @@ public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {

TypeToken<?> type = TypeToken.get(Number.class);

assertThrows(NullPointerException.class, () -> gson.getDelegateAdapter(null, type));
assertThrows(NullPointerException.class, () -> gson.getDelegateAdapter(factory1, null));

// For unknown factory the first adapter for that type should be returned
assertThat(gson.getDelegateAdapter(new DummyFactory(new DummyAdapter(0)), type)).isEqualTo(adapter2);

// For null as 'skipPast' the first adapter for that type should be returned
assertThat(gson.getDelegateAdapter(null, type)).isEqualTo(adapter2);

assertThat(gson.getDelegateAdapter(factory2, type)).isEqualTo(adapter1);
// Default Gson adapter should be returned
assertThat(gson.getDelegateAdapter(factory1, type)).isNotInstanceOf(DummyAdapter.class);
Expand Down

0 comments on commit cc4cc45

Please sign in to comment.