001/*
002 * Configurate
003 * Copyright (C) zml and Configurate contributors
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package ninja.leaping.configurate.objectmapping;
018
019import com.google.common.cache.CacheBuilder;
020import com.google.common.cache.CacheLoader;
021import com.google.common.cache.LoadingCache;
022import com.google.common.reflect.TypeToken;
023import com.google.inject.Injector;
024import org.checkerframework.checker.nullness.qual.NonNull;
025
026import javax.inject.Inject;
027import javax.inject.Singleton;
028import java.util.concurrent.ExecutionException;
029
030import static java.util.Objects.requireNonNull;
031
032/**
033 * A factory for {@link ObjectMapper}s that will inherit the injector from wherever it is provided.
034 *
035 * <p>This class is intended to be constructed through Guice dependency injection.</p>
036 */
037@Singleton
038public final class GuiceObjectMapperFactory implements ObjectMapperFactory {
039    private final LoadingCache<TypeToken<?>, ObjectMapper<?>> cache = CacheBuilder.newBuilder()
040            .weakKeys().maximumSize(512)
041            .build(new CacheLoader<TypeToken<?>, ObjectMapper<?>>() {
042                @Override
043                public ObjectMapper<?> load(TypeToken<?> key) throws Exception {
044                    return new GuiceObjectMapper<>(injector, key);
045                }
046            });
047
048    private final Injector injector;
049
050    @Inject
051    protected GuiceObjectMapperFactory(Injector baseInjector) {
052        this.injector = baseInjector;
053    }
054
055    @Override
056    public @NonNull <T> ObjectMapper<T> getMapper(@NonNull Class<T> type) throws ObjectMappingException {
057        return getMapper(TypeToken.of(type));
058    }
059
060    @NonNull
061    @Override
062    @SuppressWarnings("unchecked")
063    public <T> ObjectMapper<T> getMapper(@NonNull TypeToken<T> type) throws ObjectMappingException {
064        requireNonNull(type, "type");
065        try {
066            return (ObjectMapper<T>) cache.get(type);
067        } catch (ExecutionException e) {
068            if (e.getCause() instanceof ObjectMappingException) {
069                throw (ObjectMappingException) e.getCause();
070            } else {
071                throw new RuntimeException(e);
072            }
073        }
074    }
075
076    @Override
077    public String toString() {
078        return "GuiceObjectMapperFactory{}";
079    }
080}