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.TYPE, 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
097     * then if a parent collection is set, that collection is queried.
098     *
099     * @param token the type a serializer is required for
100     * @param <T> the type to serialize
101     * @return a serializer if any is present, or null if no applicable
102     *          serializer is found
103     * @since 4.0.0
104     */
105    @SuppressWarnings("unchecked")
106    public <T> @Nullable TypeSerializer<T> get(final TypeToken<T> token) {
107        requireNonNull(token, "type");
108        return (TypeSerializer<T>) get(token.getType());
109    }
110
111    /**
112     * Resolve a type serializer.
113     *
114     * <p>First, all registered serializers from this collection are queried
115     * then if a parent collection is set, that collection is queried.
116     *
117     * <p>This method will fail when provided a raw parameterized type</p>
118     *
119     * @param token the type a serializer is required for
120     * @param <T> the type to serialize
121     * @return a serializer if any is present, or null if no applicable
122     *          serializer is found
123     * @since 4.0.0
124     */
125    @SuppressWarnings("unchecked")
126    public <T> @Nullable TypeSerializer<T> get(final Class<T> token) {
127        requireNonNull(token, "type");
128        requireCompleteParameters(token);
129
130        return (TypeSerializer<T>) get((Type) token);
131    }
132
133    /**
134     * Resolve a type serializer.
135     *
136     * <p>First, all registered serializers from this collection are queried
137     * then if a parent collection is set, that collection is queried.
138     *
139     * @param type the type a serializer is required for
140     * @return a serializer if any is present, or null if no applicable
141     *          serializer is found
142     * @since 4.0.0
143     */
144    public @Nullable TypeSerializer<?> get(Type type) {
145        type = GenericTypeReflector.toCanonicalBoxed(annotate(requireNonNull(type, "type"))).getType();
146        @Nullable TypeSerializer<?> serial = this.typeMatches.computeIfAbsent(type, param -> {
147            for (RegisteredSerializer ent : this.serializers) {
148                if (ent.predicate.test(param)) {
149                    return ent.serializer;
150                }
151            }
152            return null;
153        });
154
155        if (serial == null && this.parent != null) {
156            serial = this.parent.get(type);
157        }
158        return serial;
159    }
160
161    /**
162     * Create a new builder to begin building a collection of type serializers
163     * that inherits from this collection.
164     *
165     * @return the new builder
166     * @since 4.0.0
167     */
168    public Builder childBuilder() {
169        return new Builder(this);
170    }
171
172    @Override
173    public String toString() {
174        return "TypeSerializerCollection{"
175                + "parent=" + this.parent
176                + ", serializers=" + this.serializers
177                + '}';
178    }
179
180    @Override public boolean equals(final Object other) {
181        if (this == other) {
182            return true;
183        }
184        if (!(other instanceof TypeSerializerCollection)) {
185            return false;
186        }
187        final TypeSerializerCollection that = (TypeSerializerCollection) other;
188        return Objects.equals(this.parent, that.parent)
189                && this.serializers.equals(that.serializers);
190    }
191
192    @Override
193    public int hashCode() {
194        return Objects.hash(this.parent, this.serializers);
195    }
196
197    /**
198     * Create a builder for a new type serializer collection without a
199     * parent set.
200     *
201     * <p>If <em>any</em> of the standard serializers provided by Configurate
202     * are desired, either the default collection or a collection inheriting
203     * from the default collection should be applied.
204     *
205     * @return the builder
206     * @since 4.0.0
207     */
208    public static Builder builder() {
209        return new Builder(null);
210    }
211
212    /**
213     * Get a collection containing all of Configurate's built-in
214     * type serializers.
215     *
216     * @return the collection
217     * @since 4.0.0
218     */
219    public static TypeSerializerCollection defaults() {
220        return DEFAULTS;
221    }
222
223    /**
224     * A builder to construct new serializer collections.
225     *
226     * @since 4.0.0
227     */
228    public static class Builder {
229        private final @Nullable TypeSerializerCollection parent;
230        private final List<RegisteredSerializer> serializers = new ArrayList<>();
231
232        Builder(final @Nullable TypeSerializerCollection parent) {
233            this.parent = parent;
234        }
235
236        /**
237         * Register a type serializer for a given type.
238         *
239         * <p>Serializers registered will match all subclasses of the provided
240         * type, as well as unwrapped primitive equivalents of the type.
241         *
242         * @param type the type to accept
243         * @param serializer the serializer that will be serialized with
244         * @param <T> the type to generify around
245         * @return this builder
246         * @since 4.0.0
247         */
248        public <T> Builder register(final TypeToken<T> type, final TypeSerializer<? super T> serializer) {
249            return register0(type.getType(), serializer);
250        }
251
252        /**
253         * Register a type serializer for a given type.
254         *
255         * <p>Serializers registered will match all subclasses of the provided
256         * type, as well as unboxed primitive equivalents of the type.
257         *
258         * @param type the type to accept
259         * @param serializer the serializer that will be serialized with
260         * @param <T> the type to generify around
261         * @return this builder
262         * @since 4.0.0
263         */
264        public <T> Builder register(final Class<T> type, final TypeSerializer<? super T> serializer) {
265            return register0(type, serializer);
266        }
267
268        /**
269         * Register a type serializer matching against a given predicate.
270         *
271         * @param test the predicate to match types against
272         * @param serializer the serializer to serialize matching types with
273         * @param <T> the type parameter
274         * @return this builder
275         * @since 4.0.0
276         */
277        public <T> Builder register(final Predicate<Type> test, final TypeSerializer<? super T> serializer) {
278            requireNonNull(test, "test");
279            requireNonNull(serializer, "serializer");
280            this.serializers.add(new RegisteredSerializer(test, serializer));
281            return this;
282        }
283
284        /**
285         * Register a scalar serializer with its own attached type token.
286         *
287         * <p>Serializers registered will match all subclasses of the provided
288         * type, as well as unboxed primitive equivalents of the type.</p>
289         *
290         * @param serializer serializer to register
291         * @param <T> value type
292         * @return this builder
293         * @since 4.0.0
294         */
295        public <T> Builder register(final ScalarSerializer<T> serializer) {
296            requireNonNull(serializer, "serializer");
297            return register(serializer.type(), serializer);
298        }
299
300        private Builder register0(final Type type, final TypeSerializer<?> serializer) {
301            requireNonNull(type, "type");
302            requireNonNull(serializer, "serializer");
303            this.serializers.add(new RegisteredSerializer(test -> {
304                // Test direct type
305                if (GenericTypeReflector.isSuperType(type, test)) {
306                    return true;
307                }
308
309                // And upper bounds
310                if (test instanceof WildcardType) {
311                    final Type[] upperBounds = ((WildcardType) test).getUpperBounds();
312                    if (upperBounds.length == 1) {
313                        return isSuperType(type, upperBounds[0]);
314                    }
315                }
316                return false;
317            }, serializer));
318            return this;
319        }
320
321        /**
322         * Register an <em>exact</em> type serializer for a given type.
323         *
324         * <p>Serializers will only match exact object types. For example, a
325         * serializer registered for {@code List<String>} would not match when
326         * {@code ArrayList<String>} is queried.</p>
327         *
328         * @param type the type to accept
329         * @param serializer the serializer that will be serialized with
330         * @param <T> the type to generify around
331         * @return this builder
332         * @since 4.0.0
333         */
334        public <T> Builder registerExact(final TypeToken<T> type, final TypeSerializer<? super T> serializer) {
335            return registerExact0(type.getType(), serializer);
336        }
337
338        /**
339         * Register an <em>exact</em> type serializer for a given type.
340         *
341         * <p>Serializers will only match exact object types. For example, a
342         * serializer registered for {@code List<String>} would not match when
343         * {@code ArrayList<String>} is queried.</p>
344         *
345         * @param type the type to accept
346         * @param serializer the serializer that will be serialized with
347         * @param <T> the type to generify around
348         * @return this builder
349         * @since 4.0.0
350         */
351        public <T> Builder registerExact(final Class<T> type, final TypeSerializer<? super T> serializer) {
352            return registerExact0(type, serializer);
353        }
354
355        /**
356         * Register a scalar serializer with its own attached type token.
357         *
358         * <p>Serializers will only match exact object types. For example, a
359         * serializer registered for {@code List<String>} would not match when
360         * {@code ArrayList<String>} is queried.</p>
361         *
362         * @param serializer serializer to register
363         * @param <T> value type
364         * @return this builder
365         * @since 4.0.0
366         */
367        public <T> Builder registerExact(final ScalarSerializer<T> serializer) {
368            requireNonNull(serializer, "serializer");
369            return registerExact(serializer.type(), serializer);
370        }
371
372        private Builder registerExact0(final Type type, final TypeSerializer<?> serializer) {
373            requireNonNull(type, "type");
374            requireNonNull(serializer, "serializer");
375            this.serializers.add(new RegisteredSerializer(test -> test.equals(type), serializer));
376            return this;
377        }
378
379        /**
380         * Register all serializers from {@code other} into this collection.
381         *
382         * <p>Creating a child collection should be preferred, but when merging
383         * multiple sets of serializers together, directly adding other
384         * collections may be the best choice.</p>
385         *
386         * @param other source collection
387         * @return this builder
388         * @since 4.0.0
389         */
390        public Builder registerAll(final TypeSerializerCollection other) {
391            this.serializers.addAll(requireNonNull(other, "other").serializers);
392            return this;
393        }
394
395        /**
396         * Register a customized object mapper to handle
397         * {@link ConfigSerializable}-annotated objects.
398         *
399         * @param factory factory to retrieve object mappers from
400         * @return this builder
401         * @since 4.0.0
402         */
403        public Builder registerAnnotatedObjects(final ObjectMapper.Factory factory) {
404            return register(Builder::isAnnotatedTarget, factory.asTypeSerializer());
405        }
406
407        /**
408         * A predicate to restrict the type serializer created by
409         * {@link ObjectMapper.Factory#asTypeSerializer()} to annotated types.
410         *
411         * @return whether a type is annotated with {@link ConfigSerializable}
412         * @since 4.0.0
413         */
414        static boolean isAnnotatedTarget(final Type type) {
415            return GenericTypeReflector.annotate(type).isAnnotationPresent(ConfigSerializable.class);
416        }
417
418        /**
419         * Create a new type serializer collection.
420         *
421         * @return a newly created collection
422         * @since 4.0.0
423         */
424        public TypeSerializerCollection build() {
425            return new TypeSerializerCollection(this.parent, this.serializers);
426        }
427    }
428
429    private static final class RegisteredSerializer {
430
431        private final Predicate<Type> predicate;
432        private final TypeSerializer<?> serializer;
433
434        private RegisteredSerializer(final Predicate<Type> predicate, final TypeSerializer<?> serializer) {
435            this.predicate = predicate;
436            this.serializer = serializer;
437        }
438
439    }
440
441}