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.serialize;
018
019import com.google.common.reflect.TypeToken;
020
021import java.util.Map;
022import java.util.concurrent.ConcurrentHashMap;
023import java.util.concurrent.CopyOnWriteArrayList;
024import java.util.function.Function;
025import java.util.function.Predicate;
026
027import static java.util.Objects.requireNonNull;
028
029/**
030 * A calculated collection of {@link TypeSerializer}s
031 */
032public class TypeSerializerCollection {
033    private final TypeSerializerCollection parent;
034    private final SerializerList serializers = new SerializerList();
035    private final Map<TypeToken<?>, TypeSerializer<?>> typeMatches = new ConcurrentHashMap<>();
036
037    TypeSerializerCollection(TypeSerializerCollection parent) {
038        this.parent = parent;
039    }
040
041    /**
042     * Gets the default {@link TypeSerializer}s. While this collection is mutable, starting with
043     * Configurate 4.0 type serializer collections will be immutable, so mutability should not be
044     * relied on. Instead, a new child of this collection should be used to register any custom
045     * serializers.
046     *
047     * @return The default serializers
048     */
049    public static TypeSerializerCollection defaults() {
050        return TypeSerializers.DEFAULT_SERIALIZERS;
051    }
052
053    public static TypeSerializerCollection create() {
054        return defaults().newChild();
055    }
056
057    @SuppressWarnings("unchecked")
058    public <T> TypeSerializer<T> get(TypeToken<T> type) {
059        type = requireNonNull(type).wrap();
060
061        TypeSerializer<?> serial = typeMatches.computeIfAbsent(type, serializers);
062        if (serial == null && parent != null) {
063            serial = parent.get(type);
064        }
065
066        return (TypeSerializer) serial;
067    }
068
069    /**
070     * Register a type serializer for a given type. Serializers registered will match all subclasses
071     * of the provided type, as well as unwrapped primitive equivalents of the type.
072     *
073     * @param type       The type to accept
074     * @param serializer The serializer that will be serialized with
075     * @param <T>        The type to generify around
076     * @return this
077     * @deprecated Use #register(TypeToken, TypeSerializer) instead
078     */
079    @Deprecated
080    public <T> TypeSerializerCollection registerType(TypeToken<T> type, TypeSerializer<? super T> serializer) {
081        return register(type, serializer);
082
083    }
084
085    /**
086     * Register a type serializer for a given type. Serializers registered will match all subclasses
087     * of the provided type, as well as unwrapped primitive equivalents of the type.
088     *
089     * @param type       The type to accept
090     * @param serializer The serializer that will be serialized with
091     * @param <T>        The type to generify around
092     * @return this
093     */
094    public <T> TypeSerializerCollection register(TypeToken<T> type, TypeSerializer<? super T> serializer) {
095        serializers.add(new RegisteredSerializer(requireNonNull(type, "type"), requireNonNull(serializer)));
096        typeMatches.clear();
097        return this;
098    }
099
100    /**
101     * Register a type serializer matching against a given predicate.
102     *
103     * @param test       The predicate to match types against
104     * @param serializer The serializer to serialize matching types with
105     * @param <T>        The type parameter
106     * @return this
107     * @deprecated Use {@link #register(Predicate, TypeSerializer)} instead
108     */
109    @Deprecated
110    public <T> TypeSerializerCollection registerPredicate(Predicate<TypeToken<T>> test, TypeSerializer<? super T> serializer) {
111        return register(test, serializer);
112    }
113
114    /**
115     * Register a type serializer matching against a given predicate.
116     *
117     * @param test       The predicate to match types against
118     * @param serializer The serializer to serialize matching types with
119     * @param <T>        The type parameter
120     * @return this
121     */
122    @SuppressWarnings("unchecked")
123    public <T> TypeSerializerCollection register(Predicate<TypeToken<T>> test, TypeSerializer<? super T> serializer) {
124        serializers.add(new RegisteredSerializer((Predicate) requireNonNull(test, "test"), requireNonNull(serializer, "serializer")));
125        typeMatches.clear();
126        return this;
127    }
128
129    /**
130     * Register a scalar serializer under its appropriate type. Serializers registered will match all subclasses
131     *
132     * @param serializer The serializer that will be serialized with
133     * @param <T>        The type to generify around
134     * @return this
135     */
136    public <T> TypeSerializerCollection register(ScalarSerializer<T> serializer) {
137        return register(serializer.type(), serializer);
138    }
139
140    public TypeSerializerCollection newChild() {
141        return new TypeSerializerCollection(this);
142    }
143
144    private static final class SerializerList extends CopyOnWriteArrayList<RegisteredSerializer> implements Function<TypeToken<?>, TypeSerializer<?>> {
145
146        @Override
147        public TypeSerializer<?> apply(TypeToken<?> type) {
148            for (RegisteredSerializer ent : this) {
149                if (ent.predicate.test(type)) {
150                    return ent.serializer;
151                }
152            }
153            return null;
154        }
155    }
156
157}