YetAnotherConfigLib

YetAnotherConfigLib

13M Downloads

GsonConfigInstance.Builder.appendGsonBuilder does not work

Crendgrim opened this issue ยท 2 comments

commented

Trying to use appendGsonBuilder to set Gson options (such as pretty printing or field naming policy) fails in an infinite recursion loop on game start.

Note for others running into the same problem: this can be worked around by using overrideGsonBuilder, but that means one has to copy the whole default setup including the (as it's private) ConfigExclusionStrategy class:

            .overrideGsonBuilder(new GsonBuilder()
                    .setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
                    .setPrettyPrinting()
                    .setExclusionStrategies(new ConfigExclusionStrategy())
                    .registerTypeHierarchyAdapter(Text.class, new Text.Serializer())
                    .registerTypeHierarchyAdapter(Style.class, new Style.Serializer())
                    .registerTypeHierarchyAdapter(Color.class, new GsonConfigInstance.ColorTypeAdapter())
                    .serializeNulls()
            )
commented

Instead of

public Builder<T> appendGsonBuilder(UnaryOperator<GsonBuilder> gsonBuilder) {
    this.gsonBuilder = builder -> gsonBuilder.apply(this.gsonBuilder.apply(builder));
    return this;
}

this would fix the issue:

public Builder<T> appendGsonBuilder(UnaryOperator<GsonBuilder> gsonBuilder) {
    final UnaryOperator<GsonBuilder> prev = gsonBuilder;
    this.gsonBuilder = builder -> operator.apply(prev.apply(builder));
    return this;
}

For anyone looking for a workaround I'ld suggest this mixin:

@Mixin(value = GsonConfigInstance.Builder.class, remap = false)
public abstract class GsonConfigInstanceBuilderMixin<T> implements GsonConfigInstanceBuilderDuck<T> {

    @Shadow
    private UnaryOperator<GsonBuilder> gsonBuilder;

    // GsonConfigInstance.Builder#appendGsonBuilder causes infinite recursion, see issue 64
    public GsonConfigInstance.Builder<T> mymodid$appendGsonBuilder(UnaryOperator<GsonBuilder> operator) {
        final UnaryOperator<GsonBuilder> prev = gsonBuilder;
        this.gsonBuilder = builder -> operator.apply(prev.apply(builder));
        //noinspection unchecked
        return (GsonConfigInstance.Builder<T>) (Object) this;
    }
}

// In a separate file
public interface GsonConfigInstanceBuilderDuck<T> {
    GsonConfigInstance.Builder<T> mymodid$appendGsonBuilder(UnaryOperator<GsonBuilder> operator);
}

which can be used as:

// just to make sure, but not really required
if (builder instanceof GsonConfigInstanceBuilderDuck) {
    //noinspection unchecked
    GsonConfigInstanceBuilderDuck<Config> duck = (GsonConfigInstanceBuilderDuck<Config>) builder;
    builder = duck.mymodid$appendGsonBuilder(b -> b.setLenient().setPrettyPrinting());
}
commented

@isXander any chance of getting Qendolin's suggestion merged?