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.objectmapping;
018
019import static java.util.Objects.requireNonNull;
020
021import org.checkerframework.checker.nullness.qual.Nullable;
022import org.spongepowered.configurate.serialize.SerializationException;
023import org.spongepowered.configurate.util.CheckedFunction;
024
025import java.lang.reflect.AnnotatedElement;
026import java.lang.reflect.AnnotatedType;
027import java.util.function.Supplier;
028
029/**
030 * Interface that gathers metadata from classes.
031 *
032 * <p>Any type of data object can be added this way.</p>
033 *
034 * @param <I> intermediate data type
035 * @since 4.0.0
036 */
037public interface FieldDiscoverer<I> {
038
039    /**
040     * Create a new field discoverer that will handle record classes.
041     *
042     * <p>This discoverer will use the record's canonical constructor to create
043     * new instances, passing {@code null} for any missing parameters. The
044     * accessor methods for each record component will be used to read
045     * values from the record.</p>
046     *
047     * @implNote To avoid requiring preview features to run on Java 14 and 15,
048     *     the record discoverer accesses methods reflectively, and can safely
049     *     be created and applied to object mappers running on any Java version.
050     *     Continued support of preview features once they have been released in
051     *     a stable Java version is not guaranteed.
052     *
053     * @return new discoverer
054     * @since 4.0.0
055     */
056    static FieldDiscoverer<?> record() {
057        return RecordFieldDiscoverer.INSTANCE;
058    }
059
060    /**
061     * Create a new discoverer for object instance fields.
062     *
063     * <p>This discoverer will process any non-static and non-transient field
064     * in the object. Modifying {@code final} fields is unsupported and may stop
065     * working with newer Java versions.</p>
066     *
067     * @param instanceFactory a factory for instance providers
068     * @return new discoverer
069     * @since 4.0.0
070     */
071    static FieldDiscoverer<?> object(final CheckedFunction<AnnotatedType, @Nullable Supplier<Object>, SerializationException> instanceFactory) {
072        return new ObjectFieldDiscoverer(requireNonNull(instanceFactory, "instanceFactory"), null);
073    }
074
075    /**
076     * Create a new discoverer for object instance fields.
077     *
078     * <p>This discoverer will process any non-static and non-transient field
079     * in the object. Modifying {@code final} fields is unsupported and may stop
080     * working with newer Java versions.</p>
081     *
082     * @param instanceFactory a factory for instance providers
083     * @param instanceUnavailableErrorMessage a message that will be part of the
084     *     exception thrown when trying to create instances for an
085     *     unsupported type
086     * @return new discoverer
087     * @since 4.1.0
088     */
089    static FieldDiscoverer<?> object(final CheckedFunction<AnnotatedType, @Nullable Supplier<Object>, SerializationException> instanceFactory,
090            final String instanceUnavailableErrorMessage) {
091        requireNonNull(instanceUnavailableErrorMessage, "instanceUnavailableErrorMessage");
092        return new ObjectFieldDiscoverer(requireNonNull(instanceFactory, "instanceFactory"), instanceUnavailableErrorMessage);
093    }
094
095    /**
096     * Create a new discoverer for object instance fields.
097     *
098     * <p>Only objects with empty constructors can be created.</p>
099     *
100     * @return new discoverer
101     * @see #object(CheckedFunction) for more details on which fields will
102     *      be discovered.
103     * @since 4.0.0
104     */
105    static FieldDiscoverer<?> emptyConstructorObject() {
106        return ObjectFieldDiscoverer.EMPTY_CONSTRUCTOR_INSTANCE;
107    }
108
109    /**
110     * Inspect the {@code target} type for fields to be supplied to
111     * the {@code collector}.
112     *
113     * <p>If the target type is handleable, a non-null value must be returned.
114     * Fields can only be collected from one source at the moment, so if the
115     * instance factory is null any discovered fields will be discarded.</p>
116     *
117     * @param target type to inspect
118     * @param collector collector for discovered fields.
119     * @param <V> object type
120     * @return a factory for handling the construction of object instances, or
121     *      {@code null} if {@code target} is not of a handleable type.
122     * @throws SerializationException if any fields have invalid data
123     * @since 4.0.0
124     */
125    <V> @Nullable InstanceFactory<I> discover(AnnotatedType target, FieldCollector<I, V> collector) throws SerializationException;
126
127    /**
128     * A handler that controls the deserialization process for an object.
129     *
130     * @param <I> intermediate type
131     * @since 4.0.0
132     */
133    interface InstanceFactory<I> {
134
135        /**
136         * Return a new instance of the intermediary type to be populated.
137         *
138         * @return new intermediate container
139         * @since 4.0.0
140         */
141        I begin();
142
143        /**
144         * Return a finalized object based on the provided intermediate.
145         *
146         * @param intermediate intermediate container to hold values
147         * @return final value
148         * @throws SerializationException if unable to construct a
149         * @since 4.0.0
150         */
151        Object complete(I intermediate) throws SerializationException;
152
153        /**
154         * Get whether or not new object instances can be created.
155         *
156         * @return new instance creation
157         * @since 4.0.0
158         */
159        boolean canCreateInstances();
160    }
161
162    /**
163     * A handler for working with mutable objects in the object mapper.
164     *
165     * @param <I> intermediate type
166     * @since 4.0.0
167     */
168    interface MutableInstanceFactory<I> extends InstanceFactory<I> {
169
170        /**
171         * Apply the intermediate data to an existing object.
172         *
173         * @param instance instance to write to
174         * @param intermediate intermediate container
175         * @throws SerializationException if unable to apply info
176         * @since 4.0.0
177         */
178        void complete(Object instance, I intermediate) throws SerializationException;
179    }
180
181    /**
182     * A collector for the necessary metadata for fields.
183     *
184     * @param <I> intermediate type
185     * @param <V> container type
186     * @since 4.0.0
187     */
188    @FunctionalInterface
189    interface FieldCollector<I, V> {
190
191        /**
192         * Accept metadata that defines a specific field.
193         *
194         * @param name name
195         * @param type declared field type, as resolved as possible
196         * @param annotations combined element containing all annotations
197         *                    applicable to the field
198         * @param deserializer a function to populate the intermediate state
199         *                     with a single deserialized field value.
200         * @param serializer a function to extract a value from a completed
201         *                   object instance.
202         * @since 4.0.0
203         */
204        void accept(String name, AnnotatedType type, AnnotatedElement annotations, FieldData.Deserializer<I> deserializer,
205                CheckedFunction<V, @Nullable Object, Exception> serializer);
206    }
207
208}