Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,16 @@ ResolvedCodec resolveCodec(
JsonTypeResolver localResolver,
JsonCodecFactory childFactory,
boolean runtimeType) {
return resolveCodec(rawType, typeRef, localResolver, childFactory, runtimeType, false);
}

ResolvedCodec resolveCodec(
Class<?> rawType,
TypeRef<?> typeRef,
JsonTypeResolver localResolver,
JsonCodecFactory childFactory,
boolean runtimeType,
boolean skipModuleFactories) {
JsonValueCodec<?> customCodec = customCodec(rawType);
if (customCodec != null) {
return new ResolvedCodec(customCodec, null);
Expand Down Expand Up @@ -944,7 +954,8 @@ ResolvedCodec resolveCodec(
if (semanticToken) {
// The exact JVM carrier codec cannot erase an explicit semantic type. The installed module
// owns that representation even when the semantic value uses a primitive carrier.
ResolvedCodec resolved = createModuleCodec(typeRef, localResolver, runtimeType);
ResolvedCodec resolved =
skipModuleFactories ? null : createModuleCodec(typeRef, localResolver, runtimeType);
if (resolved == null) {
throw new ForyJsonException("No installed JSON module owns semantic type " + typeRef);
}
Expand All @@ -955,7 +966,8 @@ ResolvedCodec resolveCodec(
return new ResolvedCodec(codec, null);
}
if (rawType == Object.class) {
ResolvedCodec resolved = createModuleCodec(typeRef, localResolver, runtimeType);
ResolvedCodec resolved =
skipModuleFactories ? null : createModuleCodec(typeRef, localResolver, runtimeType);
if (resolved != null) {
return resolved;
}
Expand Down Expand Up @@ -1009,7 +1021,8 @@ ResolvedCodec resolveCodec(
if (Path.class.isAssignableFrom(rawType)) {
return new ResolvedCodec(ScalarCodecs.PathCodec.INSTANCE, null);
}
ResolvedCodec resolved = createModuleCodec(typeRef, localResolver, runtimeType);
ResolvedCodec resolved =
skipModuleFactories ? null : createModuleCodec(typeRef, localResolver, runtimeType);
if (resolved != null) {
return resolved;
}
Expand All @@ -1034,6 +1047,10 @@ JsonValueCodec<?> createRuntimeCodec(Class<?> rawType, JsonTypeResolver localRes
return resolved == null ? null : resolved.codec;
}

boolean hasCodecFactories() {
return codecFactories.length != 0;
}

private JsonValueCodec<?> createExactCodec(
Class<?> target,
TypeRef<?> typeRef,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,11 @@ private JsonTypeInfo resolveTypeInfo(Type declaredType, Class<?> rawType, Object
}

private JsonTypeInfo resolveTypeInfo(TypeRef<?> declaredType, Object key) {
return resolveTypeInfo(declaredType, key, false);
}

private JsonTypeInfo resolveTypeInfo(
TypeRef<?> declaredType, Object key, boolean skipModuleFactories) {
validateCovariant(declaredType);
Class<?> rawType = declaredType.getRawType();
JsonTypeInfo typeInfo = customTypeInfo(declaredType, rawType);
Expand All @@ -560,7 +565,7 @@ private JsonTypeInfo resolveTypeInfo(TypeRef<?> declaredType, Object key) {
codec.resolveTypes(declaredType, this);
return typeInfo;
}
return buildTypeInfo(rawType, declaredType, key);
return buildTypeInfo(rawType, declaredType, key, null, skipModuleFactories);
}

private JsonTypeInfo resolveTypeInfo(TypeRef<?> declaredType, JsonCodec annotation) {
Expand Down Expand Up @@ -1024,7 +1029,26 @@ private JsonTypeInfo resolveRuntimeTypeInfo(Class<?> runtimeType) {
// The default Java object model is the declared schema for this exact class. Reuse that
// binding so generated capabilities stay on the direct root path. Only a codec actually
// selected by a runtime factory needs the runtime-only publication below.
typeInfo = getTypeInfo(typeRef);
if (!sharedRegistry.hasCodecFactories()) {
typeInfo = getTypeInfo(runtimeType, runtimeType);
} else {
// The runtime factory already declined this root, so the declared fallback must not ask
// the same factory again as if the root were a field occurrence.
ResolutionSnapshot snapshot = beginResolution();
try {
typeInfo = resolveTypeInfo(typeRef, resolutionTypeKey(typeRef), true);
Object key = resolutionTypeKey(typeRef);
if (typeInfos.get(key) == typeInfo) {
typeInfos.remove(key);
}
completeResolution(snapshot);
} catch (RuntimeException | Error e) {
rollbackResolution(snapshot);
throw e;
} finally {
endResolution();
}
}
runtimeTypeInfos.put(runtimeType, typeInfo);
return typeInfo;
}
Expand Down Expand Up @@ -2711,14 +2735,24 @@ private <T> ObjectCodec<T> newObjectCodec(TypeRef<T> ownerType) {
}

private JsonTypeInfo buildTypeInfo(Class<?> rawType, TypeRef<?> typeRef, Object key) {
return buildTypeInfo(rawType, typeRef, key, null);
return buildTypeInfo(rawType, typeRef, key, null, false);
}

private JsonTypeInfo buildTypeInfo(
Class<?> rawType, TypeRef<?> typeRef, Object key, JsonCodecFactory childFactory) {
return buildTypeInfo(rawType, typeRef, key, childFactory, false);
}

private JsonTypeInfo buildTypeInfo(
Class<?> rawType,
TypeRef<?> typeRef,
Object key,
JsonCodecFactory childFactory,
boolean skipModuleFactories) {
sharedRegistry.checkSecure(rawType);
JsonSharedRegistry.ResolvedCodec resolved =
sharedRegistry.resolveCodec(rawType, typeRef, this, childFactory, false);
sharedRegistry.resolveCodec(
rawType, typeRef, this, childFactory, false, skipModuleFactories);
if (resolved == null) {
return buildObjectTypeInfo(typeRef, key);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,26 @@
import org.testng.annotations.Test;

public class JsonModuleTest {
public static class FactoryPet {}

@Test
public void factoryDoesNotRequeryDeclinedRoot() {
List<Boolean> runtimeTypes = new ArrayList<>();
JsonCodecFactory factory =
(type, resolver, runtimeType) -> {
if (type.getRawType() == FactoryPet.class) {
runtimeTypes.add(runtimeType);
}
return null;
};
ForyJson json =
ForyJson.builder().withModule(context -> context.registerCodecFactory(factory)).build();

json.toJson(new FactoryPet());
json.fromJson("{}", FactoryPet.class);
assertEquals(runtimeTypes, Arrays.asList(true, false));
}

@Test
public void installsOnceAndFreezesContext() {
AtomicInteger installs = new AtomicInteger();
Expand Down
Loading