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;
018
019import static java.util.Objects.requireNonNull;
020
021import com.google.auto.value.AutoValue;
022import org.checkerframework.checker.nullness.qual.Nullable;
023import org.spongepowered.configurate.loader.ConfigurationLoader;
024import org.spongepowered.configurate.serialize.TypeSerializerCollection;
025import org.spongepowered.configurate.util.MapFactories;
026import org.spongepowered.configurate.util.MapFactory;
027import org.spongepowered.configurate.util.Types;
028import org.spongepowered.configurate.util.UnmodifiableCollections;
029
030import java.lang.reflect.Type;
031import java.util.Objects;
032import java.util.Set;
033import java.util.function.Consumer;
034
035/**
036 * This object is a holder for general configuration options.
037 *
038 * <p>This is meant to hold options that are used in configuring how the
039 * configuration data structures are handled, rather than the serialization
040 * configuration which is located in {@link ConfigurationLoader}s.</p>
041 *
042 * <p>This class is immutable.</p>
043 *
044 * @since 4.0.0
045 */
046@AutoValue
047public abstract class ConfigurationOptions {
048
049    static class Lazy {
050
051        // avoid initialization cycles
052
053        static final ConfigurationOptions DEFAULTS = new AutoValue_ConfigurationOptions(MapFactories.insertionOrdered(), null,
054                TypeSerializerCollection.defaults(), null, true, true);
055
056    }
057
058    ConfigurationOptions() {
059    }
060
061    /**
062     * Get the default set of options. This may be overridden by your chosen
063     * configuration loader, so when building configurations it is recommended
064     * to access {@code AbstractConfigurationLoader.Builder#getDefaultOptions()}
065     * instead.
066     *
067     * @return the default options
068     * @since 4.0.0
069     */
070    public static ConfigurationOptions defaults() {
071        return Lazy.DEFAULTS;
072    }
073
074    /**
075     * Gets the {@link MapFactory} specified in these options.
076     *
077     * @return the map factory
078     * @since 4.0.0
079     */
080    public abstract MapFactory mapFactory();
081
082    /**
083     * Creates a new {@link ConfigurationOptions} instance, with the specified
084     * {@link MapFactory} set, and all other settings copied from this instance.
085     *
086     * @param mapFactory the new factory to use to create a map
087     * @return the new options object
088     * @since 4.0.0
089     */
090    public ConfigurationOptions mapFactory(final MapFactory mapFactory) {
091        requireNonNull(mapFactory, "mapFactory");
092        if (this.mapFactory() == mapFactory) {
093            return this;
094        }
095        return new AutoValue_ConfigurationOptions(mapFactory, header(), serializers(), nativeTypes(),
096                shouldCopyDefaults(), implicitInitialization());
097    }
098
099    /**
100     * Gets the header specified in these options.
101     *
102     * @return the current header. Lines are split by \n, with no
103     *         trailing newline
104     * @since 4.0.0
105     */
106    public abstract @Nullable String header();
107
108    /**
109     * Creates a new {@link ConfigurationOptions} instance, with the specified
110     * header set, and all other settings copied from this instance.
111     *
112     * @param header the new header to use
113     * @return the new options object
114     * @since 4.0.0
115     */
116    public ConfigurationOptions header(final @Nullable String header) {
117        if (Objects.equals(this.header(), header)) {
118            return this;
119        }
120        return new AutoValue_ConfigurationOptions(mapFactory(), header, serializers(), nativeTypes(),
121                shouldCopyDefaults(), implicitInitialization());
122    }
123
124    /**
125     * Gets the {@link TypeSerializerCollection} specified in these options.
126     *
127     * @return the type serializers
128     * @since 4.0.0
129     */
130    public abstract TypeSerializerCollection serializers();
131
132    /**
133     * Creates a new {@link ConfigurationOptions} instance, with the specified {@link TypeSerializerCollection}
134     * set, and all other settings copied from this instance.
135     *
136     * @param serializers the serializers to use
137     * @return the new options object
138     * @since 4.0.0
139     */
140    public ConfigurationOptions serializers(final TypeSerializerCollection serializers) {
141        requireNonNull(serializers, "serializers");
142        if (this.serializers().equals(serializers)) {
143            return this;
144        }
145        return new AutoValue_ConfigurationOptions(mapFactory(), header(), serializers, nativeTypes(),
146                shouldCopyDefaults(), implicitInitialization());
147    }
148
149    /**
150     * Creates a new {@link ConfigurationOptions} instance, with a new
151     * {@link TypeSerializerCollection} created as a child of this options'
152     * current collection. The provided function will be called with the builder
153     * for this new collection to allow registering more type serializers.
154     *
155     * @param serializerBuilder accepts a builder for the collection that will
156     *                          be used in the returned options object.
157     * @return the new options object
158     * @since 4.0.0
159     */
160    public final ConfigurationOptions serializers(final Consumer<TypeSerializerCollection.Builder> serializerBuilder) {
161        requireNonNull(serializerBuilder, "serializerBuilder");
162        final TypeSerializerCollection.Builder builder = this.serializers().childBuilder();
163        serializerBuilder.accept(builder);
164        return serializers(builder.build());
165    }
166
167    @SuppressWarnings("AutoValueImmutableFields") // we don't use guava
168    abstract @Nullable Set<Class<?>> nativeTypes();
169
170    /**
171     * Creates a new {@link ConfigurationOptions} instance, with the specified native types
172     * set, and all other settings copied from this instance.
173     *
174     * <p>Native types are format-dependent, and must be provided by a
175     * configuration loader's {@link ConfigurationLoader#defaultOptions() default options}</p>
176     *
177     * <p>Null indicates that all types are accepted.</p>
178     *
179     * @param nativeTypes the types that will be accepted to a
180     *                     call to {@link ConfigurationNode#set(Object)}
181     * @return updated options object
182     * @since 4.0.0
183     */
184    public ConfigurationOptions nativeTypes(final @Nullable Set<Class<?>> nativeTypes) {
185        if (Objects.equals(this.nativeTypes(), nativeTypes)) {
186            return this;
187        }
188        return new AutoValue_ConfigurationOptions(mapFactory(), header(), serializers(),
189                nativeTypes == null ? null : UnmodifiableCollections.copyOf(nativeTypes), shouldCopyDefaults(), implicitInitialization());
190    }
191
192    /**
193     * Gets whether objects of the provided type are natively accepted as values
194     * for nodes with this as their options object.
195     *
196     * @param type the type to check
197     * @return whether the type is accepted
198     * @since 4.0.0
199     */
200    public final boolean acceptsType(final Class<?> type) {
201        requireNonNull(type, "type");
202
203        final @Nullable Set<Class<?>> nativeTypes = nativeTypes();
204
205        if (nativeTypes == null) {
206            return true;
207        }
208
209        if (nativeTypes.contains(type)) {
210            return true;
211        }
212
213        if (type.isPrimitive() && nativeTypes.contains(Types.box(type))) {
214            return true;
215        }
216
217        final Type unboxed = Types.unbox(type);
218        if (unboxed != type && nativeTypes.contains(unboxed)) {
219            return true;
220        }
221
222        for (Class<?> clazz : nativeTypes) {
223            if (clazz.isAssignableFrom(type)) {
224                return true;
225            }
226        }
227
228        return false;
229    }
230
231    /**
232     * Gets whether or not default parameters provided to {@link ConfigurationNode} getter methods
233     * should be set to the node when used.
234     *
235     * @return whether defaults should be copied into value
236     * @since 4.0.0
237     */
238    public abstract boolean shouldCopyDefaults();
239
240    /**
241     * Creates a new {@link ConfigurationOptions} instance, with the specified
242     * 'copy defaults' setting set, and all other settings copied from
243     * this instance.
244     *
245     * @param shouldCopyDefaults whether to copy defaults
246     * @return updated options object
247     * @see #shouldCopyDefaults() for information on what this method does
248     * @since 4.0.0
249     */
250    public ConfigurationOptions shouldCopyDefaults(final boolean shouldCopyDefaults) {
251        if (this.shouldCopyDefaults() == shouldCopyDefaults) {
252            return this;
253        }
254
255        return new AutoValue_ConfigurationOptions(mapFactory(), header(), serializers(), nativeTypes(),
256                shouldCopyDefaults, implicitInitialization());
257    }
258
259    /**
260     * Get whether values should be implicitly initialized.
261     *
262     * <p>When this is true, any value get operations will return an empty value
263     * rather than null. This extends through to fields loaded into
264     * object-mapped classes.</p>
265     *
266     * <p>This option is disabled by default</p>
267     *
268     * @return if implicit initialization is enabled.
269     * @since 4.0.0
270     */
271    public abstract boolean implicitInitialization();
272
273    /**
274     * Create a new {@link ConfigurationOptions} instance with the specified
275     * implicit initialization setting.
276     *
277     * @param implicitInitialization whether to initialize implicitly
278     * @return a new options object
279     * @see #implicitInitialization() for more details
280     * @since 4.0.0
281     */
282    public ConfigurationOptions implicitInitialization(final boolean implicitInitialization) {
283        if (this.implicitInitialization() == implicitInitialization) {
284            return this;
285        }
286
287        return new AutoValue_ConfigurationOptions(mapFactory(), header(), serializers(), nativeTypes(),
288                shouldCopyDefaults(), implicitInitialization);
289    }
290
291}