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, false);
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, false);
093    }
094
095    /**
096     * Create a new discoverer for object instance fields.
097     *
098     * <p>This discoverer will process any non-static and non-transient field
099     * in the object. Modifying {@code final} fields is unsupported and may stop
100     * working with newer Java versions.</p>
101     *
102     * <p>This discoverer will only match objects that it can create an instance
103     * of (i. e. where {@code instanceFactory} returns a
104     * non-{@code null} supplier).</p>
105     *
106     * @param instanceFactory a factory for instance providers
107     * @return new discoverer
108     * @since 4.2.0
109     */
110    static FieldDiscoverer<?> instantiableObject(
111        final CheckedFunction<AnnotatedType, @Nullable Supplier<Object>, SerializationException> instanceFactory
112    ) {
113        return new ObjectFieldDiscoverer(requireNonNull(instanceFactory, "instanceFactory"), null, true);
114    }
115
116    /**
117     * Create a new discoverer for object instance fields.
118     *
119     * <p>Only objects with empty constructors can be created.</p>
120     *
121     * @return new discoverer
122     * @see #object(CheckedFunction) for more details on which fields will
123     *      be discovered.
124     * @since 4.0.0
125     */
126    static FieldDiscoverer<?> emptyConstructorObject() {
127        return ObjectFieldDiscoverer.EMPTY_CONSTRUCTOR_INSTANCE;
128    }
129
130    /**
131     * Inspect the {@code target} type for fields to be supplied to
132     * the {@code collector}.
133     *
134     * <p>If the target type is handleable, a non-null value must be returned.
135     * Fields can only be collected from one source at the moment, so if the
136     * instance factory is null any discovered fields will be discarded.</p>
137     *
138     * @param target type to inspect
139     * @param collector collector for discovered fields.
140     * @param <V> object type
141     * @return a factory for handling the construction of object instances, or
142     *      {@code null} if {@code target} is not of a handleable type.
143     * @throws SerializationException if any fields have invalid data
144     * @since 4.0.0
145     */
146    <V> @Nullable InstanceFactory<I> discover(AnnotatedType target, FieldCollector<I, V> collector) throws SerializationException;
147
148    /**
149     * A handler that controls the deserialization process for an object.
150     *
151     * @param <I> intermediate type
152     * @since 4.0.0
153     */
154    interface InstanceFactory<I> {
155
156        /**
157         * Return a new instance of the intermediary type to be populated.
158         *
159         * @return new intermediate container
160         * @since 4.0.0
161         */
162        I begin();
163
164        /**
165         * Return a finalized object based on the provided intermediate.
166         *
167         * @param intermediate intermediate container to hold values
168         * @return final value
169         * @throws SerializationException if unable to construct a
170         * @since 4.0.0
171         */
172        Object complete(I intermediate) throws SerializationException;
173
174        /**
175         * Get whether or not new object instances can be created.
176         *
177         * @return new instance creation
178         * @since 4.0.0
179         */
180        boolean canCreateInstances();
181    }
182
183    /**
184     * A handler for working with mutable objects in the object mapper.
185     *
186     * @param <I> intermediate type
187     * @since 4.0.0
188     */
189    interface MutableInstanceFactory<I> extends InstanceFactory<I> {
190
191        /**
192         * Apply the intermediate data to an existing object.
193         *
194         * @param instance instance to write to
195         * @param intermediate intermediate container
196         * @throws SerializationException if unable to apply info
197         * @since 4.0.0
198         */
199        void complete(Object instance, I intermediate) throws SerializationException;
200    }
201
202    /**
203     * A collector for the necessary metadata for fields.
204     *
205     * @param <I> intermediate type
206     * @param <V> container type
207     * @since 4.0.0
208     */
209    @FunctionalInterface
210    interface FieldCollector<I, V> {
211
212        /**
213         * Accept metadata that defines a specific field.
214         *
215         * @param name name
216         * @param type declared field type, as resolved as possible
217         * @param annotations combined element containing all annotations
218         *                    applicable to the field
219         * @param deserializer a function to populate the intermediate state
220         *                     with a single deserialized field value.
221         * @param serializer a function to extract a value from a completed
222         *                   object instance.
223         * @since 4.0.0
224         */
225        void accept(String name, AnnotatedType type, AnnotatedElement annotations, FieldData.Deserializer<I> deserializer,
226                CheckedFunction<V, @Nullable Object, Exception> serializer);
227    }
228
229}