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 io.leangen.geantyref.GenericTypeReflector.box;
020import static io.leangen.geantyref.GenericTypeReflector.erase;
021
022import com.google.auto.value.AutoValue;
023import org.checkerframework.checker.nullness.qual.Nullable;
024import org.spongepowered.configurate.ConfigurationNode;
025import org.spongepowered.configurate.objectmapping.meta.Constraint;
026import org.spongepowered.configurate.objectmapping.meta.NodeResolver;
027import org.spongepowered.configurate.objectmapping.meta.Processor;
028import org.spongepowered.configurate.serialize.SerializationException;
029import org.spongepowered.configurate.serialize.TypeSerializer;
030import org.spongepowered.configurate.util.CheckedFunction;
031import org.spongepowered.configurate.util.UnmodifiableCollections;
032
033import java.lang.reflect.AnnotatedType;
034import java.util.List;
035import java.util.function.Supplier;
036
037/**
038 * Holder for field-specific information.
039 *
040 * @param <I> intermediate type
041 * @param <O> container type
042 * @since 4.0.0
043 */
044@AutoValue
045@SuppressWarnings("AutoValueImmutableFields") // we don't use guava collections
046public abstract class FieldData<I, O> {
047
048    /**
049     * Create a new data holder for a field.
050     *
051     * @param name field name
052     * @param resolvedFieldType type resolved for specific data
053     * @param constraints constraints to verify on this field
054     * @param processors processors to apply to fields backing this node
055     * @param <I> intermediate type
056     * @param <O> container type
057     * @return new field data
058     * @since 4.0.0
059     */
060    static <I, O> FieldData<I, O> of(final String name, final AnnotatedType resolvedFieldType,
061            final List<Constraint<?>> constraints, final List<Processor<?>> processors,
062            final Deserializer<I> deserializer, final CheckedFunction<O, @Nullable Object, Exception> serializer, final NodeResolver resolver) {
063        return new AutoValue_FieldData<>(name, resolvedFieldType,
064                UnmodifiableCollections.copyOf(constraints),
065                UnmodifiableCollections.copyOf(processors),
066                deserializer, serializer, resolver);
067    }
068
069    FieldData() {
070    }
071
072    /**
073     * The name of the field.
074     *
075     * @return field name
076     * @since 4.0.0
077     */
078    public abstract String name();
079
080    /**
081     * The calculated type of this field within the object type.
082     *
083     * <p>This value has had any possible type parameters resolved using
084     * information available in the context.</p>
085     *
086     * @return the resolved type
087     * @since 4.0.0
088     */
089    public abstract AnnotatedType resolvedType();
090
091    abstract List<Constraint<?>> constraints();
092
093    abstract List<Processor<?>> processors();
094
095    abstract Deserializer<I> deserializer();
096
097    abstract CheckedFunction<O, @Nullable Object, Exception> serializer();
098
099    abstract NodeResolver nodeResolver();
100
101    /**
102     * Test if an object would be valid for this field.
103     *
104     * @param instance instance to validate
105     * @return true if valid
106     * @since 4.0.0
107     */
108    public boolean isValid(final Object instance) {
109        try {
110            validate(instance);
111            return true;
112        } catch (final SerializationException ex) {
113            return false;
114        }
115    }
116
117    /**
118     * Try to ensure the provided value is acceptable.
119     *
120     * @param instance field value instance to validate
121     * @throws SerializationException if validation fails
122     * @since 4.0.0
123     */
124    @SuppressWarnings("unchecked")
125    public void validate(final @Nullable Object instance) throws SerializationException {
126        if (instance != null && !erase(box(resolvedType().getType())).isInstance(instance)) {
127            throw new SerializationException("Object " + instance + " is not of expected type " + resolvedType().getType());
128        }
129
130        for (final Constraint<?> constraint : constraints()) {
131            ((Constraint<Object>) constraint).validate(instance);
132        }
133    }
134
135    TypeSerializer<?> serializerFrom(final ConfigurationNode node) throws SerializationException {
136        final @Nullable TypeSerializer<?> serial = node.options().serializers().get(resolvedType());
137        if (serial == null) {
138            throw new SerializationException("No TypeSerializer found for field " + name() + " of type " + resolvedType().getType());
139        }
140        return serial;
141    }
142
143    /**
144     * Use this field's node resolvers to determine a target node.
145     *
146     * @param source parent node
147     * @return resolved node
148     * @since 4.0.0
149     */
150    public @Nullable ConfigurationNode resolveNode(final ConfigurationNode source) {
151        return this.nodeResolver().resolve(source);
152    }
153
154    /**
155     * A deserialization handler to appropriately place object data into fields.
156     *
157     * @param <I> intermediate data type
158     * @since 4.0.0
159     */
160    @FunctionalInterface
161    public interface Deserializer<I> {
162
163        /**
164         * Apply either a new value or implicit initializer to the
165         * {@code intermediate} object as appropriate.
166         *
167         * @param intermediate the intermediate container
168         * @param newValue new value to store
169         * @param implicitInitializer the implicit initializer
170         * @since 4.0.0
171         */
172        void accept(I intermediate, @Nullable Object newValue, Supplier<@Nullable Object> implicitInitializer);
173    }
174
175}