blob: b5beb7880a0ed814868b43f854a437d50496bff9 [file] [log] [blame]
// Copyright 2023 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package com.google.devtools.build.lib.bazel.bzlmod;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import javax.annotation.Nullable;
import net.starlark.java.eval.Dict;
/**
* Creates Gson type adapters for parameterized types by using a delegate parameterized type that
* already has a registered type adapter factory.
*/
public final class DelegateTypeAdapterFactory<I, R extends I, D extends I>
implements TypeAdapterFactory {
private final Class<R> rawType;
private final Class<I> intermediateToDelegateType;
private final Class<D> delegateType;
private final Function<R, D> rawToDelegate;
private final Function<D, R> delegateToRaw;
private DelegateTypeAdapterFactory(
Class<R> rawType,
Class<I> intermediateToDelegateType,
Class<D> delegateType,
Function<R, D> rawToDelegate,
Function<D, R> delegateToRaw) {
this.rawType = rawType;
this.intermediateToDelegateType = intermediateToDelegateType;
this.delegateType = delegateType;
this.rawToDelegate = rawToDelegate;
this.delegateToRaw = delegateToRaw;
}
public static final TypeAdapterFactory IMMUTABLE_MAP =
new DelegateTypeAdapterFactory<>(
ImmutableMap.class,
Map.class,
LinkedHashMap.class,
raw -> new LinkedHashMap<>((Map<?, ?>) raw),
delegate -> ImmutableMap.copyOf((Map<?, ?>) delegate));
public static final TypeAdapterFactory IMMUTABLE_BIMAP =
new DelegateTypeAdapterFactory<>(
ImmutableBiMap.class,
Map.class,
LinkedHashMap.class,
raw -> new LinkedHashMap<>((Map<?, ?>) raw),
delegate -> ImmutableBiMap.copyOf((Map<?, ?>) delegate));
public static final TypeAdapterFactory DICT =
new DelegateTypeAdapterFactory<>(
Dict.class,
Map.class,
LinkedHashMap.class,
raw -> new LinkedHashMap<>((Map<?, ?>) raw),
delegate -> Dict.immutableCopyOf((Map<?, ?>) delegate));
public static final TypeAdapterFactory IMMUTABLE_LIST =
new DelegateTypeAdapterFactory<>(
ImmutableList.class,
List.class,
ArrayList.class,
raw -> new ArrayList<>((List<?>) raw),
delegate -> ImmutableList.copyOf((List<?>) delegate));
public static final TypeAdapterFactory IMMUTABLE_SET =
new DelegateTypeAdapterFactory<>(
ImmutableSet.class,
Set.class,
LinkedHashSet.class,
raw -> new LinkedHashSet<>((Set<?>) raw),
delegate -> ImmutableSet.copyOf((Set<?>) delegate));
@SuppressWarnings("unchecked")
@Override
@Nullable
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (typeToken.getRawType() != rawType || !(type instanceof ParameterizedType)) {
return null;
}
com.google.common.reflect.TypeToken<?> betterToken =
com.google.common.reflect.TypeToken.of(typeToken.getType());
final TypeAdapter<Object> delegateAdapter =
(TypeAdapter<Object>)
gson.getAdapter(
TypeToken.get(
betterToken
.getSupertype((Class<Object>) intermediateToDelegateType)
.getSubtype(delegateType)
.getType()));
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
delegateAdapter.write(out, rawToDelegate.apply((R) value));
}
@Override
public T read(JsonReader in) throws IOException {
return (T) delegateToRaw.apply((D) delegateAdapter.read(in));
}
};
}
}