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 org.spongepowered.configurate.serialize;
018
019import static io.leangen.geantyref.GenericTypeReflector.annotate;
020import static io.leangen.geantyref.GenericTypeReflector.isSuperType;
021import static java.util.Objects.requireNonNull;
022import static org.spongepowered.configurate.util.Types.requireCompleteParameters;
023
024import io.leangen.geantyref.GenericTypeReflector;
025import io.leangen.geantyref.TypeToken;
026import org.checkerframework.checker.nullness.qual.Nullable;
027import org.spongepowered.configurate.objectmapping.ConfigSerializable;
028import org.spongepowered.configurate.objectmapping.ObjectMapper;
029import org.spongepowered.configurate.util.UnmodifiableCollections;
030
031import java.lang.reflect.Type;
032import java.lang.reflect.WildcardType;
033import java.util.ArrayList;
034import java.util.List;
035import java.util.Map;
036import java.util.Objects;
037import java.util.concurrent.ConcurrentHashMap;
038import java.util.function.Predicate;
039
040/**
041 * A calculated collection of {@link TypeSerializer}s.
042 *
043 * @since 4.0.0
044 */
045public final class TypeSerializerCollection {
046
047    private static final TypeSerializerCollection DEFAULTS;
048
049    static {
050        DEFAULTS = TypeSerializerCollection.builder()
051                .registerExact(Scalars.STRING)
052                .registerExact(Scalars.BOOLEAN)
053                .register(MapSerializer.TYPE, new MapSerializer())
054                .register(ListSerializer.TYPE, new ListSerializer())
055                .registerExact(Scalars.BYTE)
056                .registerExact(Scalars.SHORT)
057                .registerExact(Scalars.INTEGER)
058                .registerExact(Scalars.LONG)
059                .registerExact(Scalars.FLOAT)
060                .registerExact(Scalars.DOUBLE)
061                .registerAnnotatedObjects(ObjectMapper.factory())
062                .register(Scalars.ENUM)
063                .registerExact(Scalars.CHAR)
064                .registerExact(Scalars.URI)
065                .registerExact(Scalars.URL)
066                .registerExact(Scalars.UUID)
067                .registerExact(Scalars.PATTERN)
068                .register(ArraySerializer.Objects::accepts, new ArraySerializer.Objects())
069                .registerExact(ArraySerializer.Booleans.TYPE, new ArraySerializer.Booleans())
070                .registerExact(ArraySerializer.Bytes.TYPE, new ArraySerializer.Bytes())
071                .registerExact(ArraySerializer.Chars.TYPE, new ArraySerializer.Chars())
072                .registerExact(ArraySerializer.Shorts.TYPE, new ArraySerializer.Shorts())
073                .registerExact(ArraySerializer.Ints.TYPE, new ArraySerializer.Ints())
074                .registerExact(ArraySerializer.Longs.TYPE, new ArraySerializer.Longs())
075                .registerExact(ArraySerializer.Floats.TYPE, new ArraySerializer.Floats())
076                .registerExact(ArraySerializer.Doubles.TYPE, new ArraySerializer.Doubles())
077                .register(SetSerializer::accepts, new SetSerializer())
078                .register(ConfigurationNodeSerializer.TYPE, new ConfigurationNodeSerializer())
079                .register(PathSerializer.TYPE, PathSerializer.INSTANCE)
080                .registerExact(FileSerializer.TYPE, FileSerializer.INSTANCE)
081                .build();
082    }
083
084    private final @Nullable TypeSerializerCollection parent;
085    private final List<RegisteredSerializer> serializers;
086    private final Map<Type, TypeSerializer<?>> typeMatches = new ConcurrentHashMap<>();
087
088    private TypeSerializerCollection(final @Nullable TypeSerializerCollection parent, final List<RegisteredSerializer> serializers) {
089        this.parent = parent;
090        this.serializers = UnmodifiableCollections.copyOf(serializers);
091    }
092
093    /**
094     * Resolve a type serializer.
095     *
096     * <p>First, all registered serializers from this collection are queried in
097     * registration order, then if a parent collection is set, that collection
098     * is queried.</p>
099     *
100     * @param token the type a serializer is required for
101     * @param <T> the type to serialize
102     * @return a serializer if any is present, or null if no applicable
103     *          serializer is found
104     * @since 4.0.0
105     */
106    @SuppressWarnings("unchecked")
107    public <T> @Nullable TypeSerializer<T> get(final TypeToken<T> token) {
108        requireNonNull(token, "type");
109        return (TypeSerializer<T>) get(token.getType());
110    }
111
112    /**
113     * Resolve a type serializer.
114     *
115     * <p>First, all registered serializers from this collection are queried in
116     * registration order, then if a parent collection is set, that collection
117     * is queried.</p>
118     *
119     * <p>This method will fail when provided a raw parameterized type</p>
120     *
121     * @param token the type a serializer is required for
122     * @param <T> the type to serialize
123     * @return a serializer if any is present, or null if no applicable
124     *          serializer is found
125     * @since 4.0.0
126     */
127    @SuppressWarnings("unchecked")
128    public <T> @Nullable TypeSerializer<T> get(final Class<T> token) {
129        requireNonNull(token, "type");
130        requireCompleteParameters(token);
131
132        return (TypeSerializer<T>) get((Type) token);
133    }
134
135    /**
136     * Resolve a type serializer.
137     *
138     * <p>First, all registered serializers from this collection are queried
139     * then if a parent collection is set, that collection is queried.
140     *
141     * @param type the type a serializer is required for
142     * @return a serializer if any is present, or null if no applicable
143     *          serializer is found
144     * @since 4.0.0
145     */
146    public @Nullable TypeSerializer<?> get(Type type) {
147        type = GenericTypeReflector.toCanonicalBoxed(annotate(requireNonNull(type, "type"))).getType();
148        @Nullable TypeSerializer<?> serial = this.typeMatches.computeIfAbsent(type, param -> {
149            for (RegisteredSerializer ent : this.serializers) {
150                if (ent.predicate.test(param)) {
151                    return ent.serializer;
152                }
153            }
154            return null;
155        });
156
157        if (serial == null && this.parent != null) {
158            serial = this.parent.get(type);
159        }
160        return serial;
161    }
162
163    /**
164     * Create a new builder to begin building a collection of type serializers
165     * that inherits from this collection.
166     *
167     * @return the new builder
168     * @since 4.0.0
169     */
170    public Builder childBuilder() {
171        return new Builder(this);
172    }
173
174    @Override
175    public String toString() {
176        return "TypeSerializerCollection{"
177                + "parent=" + this.parent
178                + ", serializers=" + this.serializers
179                + '}';
180    }
181
182    @Override public boolean equals(final Object other) {
183        if (this == other) {
184            return true;
185        }
186        if (!(other instanceof TypeSerializerCollection)) {
187            return false;
188        }
189        final TypeSerializerCollection that = (TypeSerializerCollection) other;
190        return Objects.equals(this.parent, that.parent)
191                && this.serializers.equals(that.serializers);
192    }
193
194    @Override
195    public int hashCode() {
196        return Objects.hash(this.parent, this.serializers);
197    }
198
199    /**
200     * Create a builder for a new type serializer collection without a
201     * parent set.
202     *
203     * <p>If <em>any</em> of the standard serializers provided by Configurate
204     * are desired, either the default collection or a collection inheriting
205     * from the default collection should be applied.
206     *
207     * @return the builder
208     * @since 4.0.0
209     */
210    public static Builder builder() {
211        return new Builder(null);
212    }
213
214    /**
215     * Get a collection containing all of Configurate's built-in
216     * type serializers.
217     *
218     * @return the collection
219     * @since 4.0.0
220     */
221    public static TypeSerializerCollection defaults() {
222        return DEFAULTS;
223    }
224
225    /**
226     * A builder to construct new serializer collections.
227     *
228     * <p>Serializers added to a builder will be prioritized based on
229     * registration order, so if multiple serializers could match a single type,
230     * the first-registered one will be used.</p>
231     *
232     * @since 4.0.0
233     */
234    public static class Builder {
235        private final @Nullable TypeSerializerCollection parent;
236        private final List<RegisteredSerializer> serializers = new ArrayList<>();
237
238        Builder(final @Nullable TypeSerializerCollection parent) {
239            this.parent = parent;
240        }
241
242        /**
243         * Register a type serializer for a given type.
244         *
245         * <p>Serializers registered will match all subclasses of the provided
246         * type, as well as unwrapped primitive equivalents of the type.
247         *
248         * @param type the type to accept
249         * @param serializer the serializer that will be serialized with
250         * @param <T> the type to generify around
251         * @return this builder
252         * @since 4.0.0
253         */
254        public <T> Builder register(final TypeToken<T> type, final TypeSerializer<? super T> serializer) {
255            return register0(type.getType(), serializer);
256        }
257
258        /**
259         * Register a type serializer for a given type.
260         *
261         * <p>Serializers registered will match all subclasses of the provided
262         * type, as well as unboxed primitive equivalents of the type.
263         *
264         * @param type the type to accept
265         * @param serializer the serializer that will be serialized with
266         * @param <T> the type to generify around
267         * @return this builder
268         * @since 4.0.0
269         */
270        public <T> Builder register(final Class<T> type, final TypeSerializer<? super T> serializer) {
271            return register0(type, serializer);
272        }
273
274        /**
275         * Register a type serializer matching against a given predicate.
276         *
277         * @param test the predicate to match types against
278         * @param serializer the serializer to serialize matching types with
279         * @param <T> the type parameter
280         * @return this builder
281         * @since 4.0.0
282         */
283        public <T> Builder register(final Predicate<Type> test, final TypeSerializer<? super T> serializer) {
284            requireNonNull(test, "test");
285            requireNonNull(serializer, "serializer");
286            this.serializers.add(new RegisteredSerializer(test, serializer));
287            return this;
288        }
289
290        /**
291         * Register a scalar serializer with its own attached type token.
292         *
293         * <p>Serializers registered will match all subclasses of the provided
294         * type, as well as unboxed primitive equivalents of the type.</p>
295         *
296         * @param serializer serializer to register
297         * @param <T> value type
298         * @return this builder
299         * @since 4.0.0
300         */
301        public <T> Builder register(final ScalarSerializer<T> serializer) {
302            requireNonNull(serializer, "serializer");
303            return register(serializer.type(), serializer);
304        }
305
306        private Builder register0(final Type type, final TypeSerializer<?> serializer) {
307            requireNonNull(type, "type");
308            requireNonNull(serializer, "serializer");
309            this.serializers.add(new RegisteredSerializer(test -> {
310                // Test direct type
311                if (GenericTypeReflector.isSuperType(type, test)) {
312                    return true;
313                }
314
315                // And upper bounds
316                if (test instanceof WildcardType) {
317                    final Type[] upperBounds = ((WildcardType) test).getUpperBounds();
318                    if (upperBounds.length == 1) {
319                        return isSuperType(type, upperBounds[0]);
320                    }
321                }
322                return false;
323            }, serializer));
324            return this;
325        }
326
327        /**
328         * Register an <em>exact</em> type serializer for a given type.
329         *
330         * <p>Serializers will only match exact object types. For example, a
331         * serializer registered for {@code List<String>} would not match when
332         * {@code ArrayList<String>} is queried.</p>
333         *
334         * @param type the type to accept
335         * @param serializer the serializer that will be serialized with
336         * @param <T> the type to generify around
337         * @return this builder
338         * @since 4.0.0
339         */
340        public <T> Builder registerExact(final TypeToken<T> type, final TypeSerializer<? super T> serializer) {
341            return registerExact0(type.getType(), serializer);
342        }
343
344        /**
345         * Register an <em>exact</em> type serializer for a given type.
346         *
347         * <p>Serializers will only match exact object types. For example, a
348         * serializer registered for {@code List<String>} would not match when
349         * {@code ArrayList<String>} is queried.</p>
350         *
351         * @param type the type to accept
352         * @param serializer the serializer that will be serialized with
353         * @param <T> the type to generify around
354         * @return this builder
355         * @since 4.0.0
356         */
357        public <T> Builder registerExact(final Class<T> type, final TypeSerializer<? super T> serializer) {
358            return registerExact0(type, serializer);
359        }
360
361        /**
362         * Register a scalar serializer with its own attached type token.
363         *
364         * <p>Serializers will only match exact object types. For example, a
365         * serializer registered for {@code List<String>} would not match when
366         * {@code ArrayList<String>} is queried.</p>
367         *
368         * @param serializer serializer to register
369         * @param <T> value type
370         * @return this builder
371         * @since 4.0.0
372         */
373        public <T> Builder registerExact(final ScalarSerializer<T> serializer) {
374            requireNonNull(serializer, "serializer");
375            return registerExact(serializer.type(), serializer);
376        }
377
378        private Builder registerExact0(final Type type, final TypeSerializer<?> serializer) {
379            requireNonNull(type, "type");
380            requireNonNull(serializer, "serializer");
381            this.serializers.add(new RegisteredSerializer(test -> test.equals(type), serializer));
382            return this;
383        }
384
385        /**
386         * Register all serializers from {@code other} into this collection.
387         *
388         * <p>Creating a child collection should be preferred, but when merging
389         * multiple sets of serializers together, directly adding other
390         * collections may be the best choice.</p>
391         *
392         * @param other source collection
393         * @return this builder
394         * @since 4.0.0
395         */
396        public Builder registerAll(final TypeSerializerCollection other) {
397            this.serializers.addAll(requireNonNull(other, "other").serializers);
398            return this;
399        }
400
401        /**
402         * Register a customized object mapper to handle
403         * {@link ConfigSerializable}-annotated objects.
404         *
405         * @param factory factory to retrieve object mappers from
406         * @return this builder
407         * @since 4.0.0
408         */
409        public Builder registerAnnotatedObjects(final ObjectMapper.Factory factory) {
410            return register(Builder::isAnnotatedTarget, factory.asTypeSerializer());
411        }
412
413        /**
414         * A predicate to restrict the type serializer created by
415         * {@link ObjectMapper.Factory#asTypeSerializer()} to annotated types.
416         *
417         * @return whether a type is annotated with {@link ConfigSerializable}
418         * @since 4.0.0
419         */
420        static boolean isAnnotatedTarget(final Type type) {
421            return GenericTypeReflector.annotate(type).isAnnotationPresent(ConfigSerializable.class);
422        }
423
424        /**
425         * Create a new type serializer collection.
426         *
427         * @return a newly created collection
428         * @since 4.0.0
429         */
430        public TypeSerializerCollection build() {
431            return new TypeSerializerCollection(this.parent, this.serializers);
432        }
433    }
434
435    private static final class RegisteredSerializer {
436
437        private final Predicate<Type> predicate;
438        private final TypeSerializer<?> serializer;
439
440        private RegisteredSerializer(final Predicate<Type> predicate, final TypeSerializer<?> serializer) {
441            this.predicate = predicate;
442            this.serializer = serializer;
443        }
444
445    }
446
447}