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 ninja.leaping.configurate;
018
019import com.google.common.collect.ImmutableSet;
020import com.google.common.primitives.Primitives;
021import ninja.leaping.configurate.loader.ConfigurationLoader;
022import ninja.leaping.configurate.objectmapping.DefaultObjectMapperFactory;
023import ninja.leaping.configurate.objectmapping.ObjectMapperFactory;
024import ninja.leaping.configurate.objectmapping.serialize.TypeSerializerCollection;
025import ninja.leaping.configurate.util.MapFactories;
026import ninja.leaping.configurate.util.MapFactory;
027import org.checkerframework.checker.nullness.qual.NonNull;
028import org.checkerframework.checker.nullness.qual.Nullable;
029
030import java.util.Objects;
031import java.util.Set;
032import java.util.function.Consumer;
033
034import static java.util.Objects.requireNonNull;
035
036/**
037 * This object is a holder for general configuration options.
038 *
039 * <p>This is meant to hold options that are used in configuring how the configuration data
040 * structures are handled, rather than the serialization configuration that is located in {@link
041 * ConfigurationLoader}s.</p>
042 *
043 * <p>This class is immutable.</p>
044 */
045public class ConfigurationOptions {
046    private static final ConfigurationOptions DEFAULTS = new ConfigurationOptions(MapFactories.insertionOrdered(), null,
047            TypeSerializerCollection.defaults(), null, DefaultObjectMapperFactory.getInstance(), false);
048    @NonNull
049    private final MapFactory mapFactory;
050    @Nullable
051    private final String header;
052    @NonNull
053    private final TypeSerializerCollection serializers;
054    @Nullable
055    private final ImmutableSet<Class<?>> acceptedTypes;
056    @NonNull
057    private final ObjectMapperFactory objectMapperFactory;
058    private final boolean shouldCopyDefaults;
059
060    private ConfigurationOptions(@NonNull MapFactory mapFactory, @Nullable String header, @NonNull TypeSerializerCollection serializers, @Nullable Set<Class<?>> acceptedTypes, @NonNull ObjectMapperFactory objectMapperFactory, boolean shouldCopyDefaults) {
061        this.mapFactory = mapFactory;
062        this.header = header;
063        this.serializers = serializers;
064        this.acceptedTypes = acceptedTypes == null ? null : ImmutableSet.copyOf(acceptedTypes);
065        this.objectMapperFactory = objectMapperFactory;
066        this.shouldCopyDefaults = shouldCopyDefaults;
067    }
068
069    /**
070     * Get the default set of options. This may be overridden by your chosen configuration loader, so when building
071     * configurations it is recommended to access {@code AbstractConfigurationLoader.Builder#getDefaultOptions()}
072     * instead.
073     *
074     * @return the default options
075     */
076    @NonNull
077    public static ConfigurationOptions defaults() {
078        return DEFAULTS;
079    }
080
081    /**
082     * Gets the {@link MapFactory} specified in these options.
083     *
084     * @return The map factory
085     */
086    @NonNull
087    public MapFactory getMapFactory() {
088        return mapFactory;
089    }
090
091    /**
092     * Creates a new {@link ConfigurationOptions} instance, with the specified {@link MapFactory} set, and all other
093     * settings copied from this instance.
094     *
095     * @param mapFactory The new factory to use to create a map
096     * @return The new options object
097     * @deprecated USe {@link #withMapFactory(MapFactory)} instead
098     */
099    @NonNull
100    @Deprecated
101    public ConfigurationOptions setMapFactory(@NonNull MapFactory mapFactory) {
102        return withMapFactory(mapFactory);
103    }
104
105    /**
106     * Creates a new {@link ConfigurationOptions} instance, with the specified {@link MapFactory} set, and all other
107     * settings copied from this instance.
108     *
109     * @param mapFactory The new factory to use to create a map
110     * @return The new options object
111     */
112    @NonNull
113    public ConfigurationOptions withMapFactory(@NonNull MapFactory mapFactory) {
114        requireNonNull(mapFactory, "mapFactory");
115        if (this.mapFactory == mapFactory) {
116            return this;
117        }
118        return new ConfigurationOptions(mapFactory, header, serializers, acceptedTypes, objectMapperFactory, shouldCopyDefaults);
119    }
120
121    /**
122     * Gets the header specified in these options.
123     *
124     * @return The current header. Lines are split by \n,
125     */
126    @Nullable
127    public String getHeader() {
128        return this.header;
129    }
130
131    /**
132     * Creates a new {@link ConfigurationOptions} instance, with the specified header set, and all other settings copied
133     * from this instance.
134     *
135     * @param header The new header to use
136     * @return The new options object
137     */
138    @NonNull
139    public ConfigurationOptions setHeader(@Nullable String header) {
140        return withHeader(header);
141    }
142
143    /**
144     * Creates a new {@link ConfigurationOptions} instance, with the specified header set, and all other settings copied
145     * from this instance.
146     *
147     * @param header The new header to use
148     * @return The new options object
149     */
150    @NonNull
151    public ConfigurationOptions withHeader(@Nullable String header) {
152        if (Objects.equals(this.header, header)) {
153            return this;
154        }
155        return new ConfigurationOptions(mapFactory, header, serializers, acceptedTypes, objectMapperFactory, shouldCopyDefaults);
156    }
157
158    /**
159     * Gets the {@link TypeSerializerCollection} specified in these options.
160     *
161     * @return The type serializers
162     */
163    @NonNull
164    public TypeSerializerCollection getSerializers() {
165        return this.serializers;
166    }
167
168    /**
169     * Creates a new {@link ConfigurationOptions} instance, with the specified {@link TypeSerializerCollection} set, and
170     * all other settings copied from this instance.
171     *
172     * @param serializers The serializers to use
173     * @return The new options object
174     * @deprecated Use {@link #withSerializers(TypeSerializerCollection)} instead
175     */
176    @NonNull
177    @Deprecated
178    public ConfigurationOptions setSerializers(@NonNull TypeSerializerCollection serializers) {
179        return withSerializers(serializers);
180    }
181
182    /**
183     * Creates a new {@link ConfigurationOptions} instance, with the specified {@link TypeSerializerCollection} set, and
184     * all other settings copied from this instance.
185     *
186     * @param serializers The serializers to use
187     * @return The new options object
188     */
189    @NonNull
190    public ConfigurationOptions withSerializers(@NonNull TypeSerializerCollection serializers) {
191        requireNonNull(serializers, "serializers");
192        if (this.serializers == serializers) {
193            return this;
194        }
195        return new ConfigurationOptions(mapFactory, header, serializers, acceptedTypes, objectMapperFactory, shouldCopyDefaults);
196    }
197
198    /**
199     * Creates a new {@link ConfigurationOptions} instance, with a new {@link TypeSerializerCollection} created as a
200     * child of this options' current collection. The provided function will be called with the builder for this new
201     * collection to allow registering more type serializers.
202     *
203     * @param serializerBuilder accepts a builder for the collection that will be used in the returned options object.
204     * @return The new options object
205     */
206    public @NonNull ConfigurationOptions withSerializers(@NonNull Consumer<TypeSerializerCollection> serializerBuilder) {
207        requireNonNull(serializerBuilder, "serializerBuilder");
208        final TypeSerializerCollection builder = this.serializers.newChild();
209        serializerBuilder.accept(builder);
210        return new ConfigurationOptions(mapFactory, header, builder, acceptedTypes, objectMapperFactory, shouldCopyDefaults);
211    }
212
213    /**
214     * Gets the {@link ObjectMapperFactory} specified in these options.
215     *
216     * @return The factory used to construct ObjectMapper instances
217     */
218    @NonNull
219    public ObjectMapperFactory getObjectMapperFactory() {
220        return this.objectMapperFactory;
221    }
222
223    /**
224     * Creates a new {@link ConfigurationOptions} instance, with the specified {@link ObjectMapperFactory} set, and all
225     * other settings copied from this instance.
226     *
227     * @param objectMapperFactory The factory to use to produce object mapper instances. Must not be null
228     * @return updated options object
229     * @deprecated Use {@link #withObjectMapperFactory(ObjectMapperFactory)} instead
230     */
231    @NonNull
232    @Deprecated
233    public ConfigurationOptions setObjectMapperFactory(@NonNull ObjectMapperFactory objectMapperFactory) {
234        return withObjectMapperFactory(objectMapperFactory);
235    }
236
237    /**
238     * Creates a new {@link ConfigurationOptions} instance, with the specified {@link ObjectMapperFactory} set, and all
239     * other settings copied from this instance.
240     *
241     * @param objectMapperFactory The factory to use to produce object mapper instances. Must not be null
242     * @return updated options object
243     */
244    @NonNull
245    public ConfigurationOptions withObjectMapperFactory(@NonNull ObjectMapperFactory objectMapperFactory) {
246        requireNonNull(objectMapperFactory, "factory");
247        if (this.objectMapperFactory == objectMapperFactory) {
248            return this;
249        }
250        return new ConfigurationOptions(mapFactory, header, serializers, acceptedTypes, objectMapperFactory, shouldCopyDefaults);
251    }
252
253    /**
254     * Gets whether objects of the provided type are accepted as values for nodes with this as their options object.
255     *
256     * @param type The type to check
257     * @return Whether the type is accepted
258     */
259    public boolean acceptsType(@NonNull Class<?> type) {
260        requireNonNull(type, "type");
261
262        if (this.acceptedTypes == null) {
263            return true;
264        }
265        if (this.acceptedTypes.contains(type)) {
266            return true;
267        }
268
269        if (type.isPrimitive() && this.acceptedTypes.contains(Primitives.wrap(type))) {
270            return true;
271        }
272
273        if (Primitives.isWrapperType(type) && this.acceptedTypes.contains(Primitives.unwrap(type))) {
274            return true;
275        }
276
277        for (Class<?> clazz : this.acceptedTypes) {
278            if (clazz.isAssignableFrom(type)) {
279                return true;
280            }
281        }
282
283        return false;
284    }
285
286    /**
287     * Creates a new {@link ConfigurationOptions} instance, with the specified accepted types set, and all other
288     * settings copied from this instance.
289     *
290     * <p>'Accepted types' are types which are accepted as native values for the configuration.</p>
291     *
292     * <p>Null indicates that all types are accepted.</p>
293     *
294     * @param acceptedTypes The types that will be accepted to a call to {@link ConfigurationNode#setValue(Object)}
295     * @return updated options object
296     * @deprecated Use {@link #withNativeTypes(Set)} instead
297     */
298    @NonNull
299    @Deprecated
300    public ConfigurationOptions setAcceptedTypes(@Nullable Set<Class<?>> acceptedTypes) {
301        return withNativeTypes(acceptedTypes);
302    }
303
304    /**
305     * Creates a new {@link ConfigurationOptions} instance, with the specified native types set, and all other settings
306     * copied from this instance.
307     * <p>
308     * Native types are format-dependent, and must be provided by a configuration loader's {@link
309     * ConfigurationLoader#getDefaultOptions() default options}.
310     *
311     * <p>Null indicates that all types are accepted. Setting this may result in unexpected changes to values</p>
312     *
313     * @param acceptedTypes The types that will be accepted to a call to {@link ConfigurationNode#setValue(Object)}
314     * @return updated options object
315     */
316    @NonNull
317    public ConfigurationOptions withNativeTypes(@Nullable Set<Class<?>> acceptedTypes) {
318        if (Objects.equals(this.acceptedTypes, acceptedTypes)) {
319            return this;
320        }
321
322        return new ConfigurationOptions(mapFactory, header, serializers, acceptedTypes, objectMapperFactory, shouldCopyDefaults);
323    }
324
325    /**
326     * Gets whether or not default parameters provided to {@link ConfigurationNode} getter methods should be set to the
327     * node when used.
328     *
329     * @return Whether defaults should be copied into value
330     */
331    public boolean shouldCopyDefaults() {
332        return shouldCopyDefaults;
333    }
334
335    /**
336     * Creates a new {@link ConfigurationOptions} instance, with the specified 'copy defaults' setting set, and all
337     * other settings copied from this instance.
338     *
339     * @param shouldCopyDefaults whether to copy defaults
340     * @return updated options object
341     * @see #shouldCopyDefaults() for information on what this method does
342     * @deprecated Use {@link #withShouldCopyDefaults(boolean)}
343     */
344    @NonNull
345    @Deprecated
346    public ConfigurationOptions setShouldCopyDefaults(boolean shouldCopyDefaults) {
347        return withShouldCopyDefaults(shouldCopyDefaults);
348    }
349
350    /**
351     * Creates a new {@link ConfigurationOptions} instance, with the specified 'copy defaults' setting set, and all
352     * other settings copied from this instance.
353     *
354     * @param shouldCopyDefaults wether to copy defaults
355     * @return updated options object
356     * @see #shouldCopyDefaults() for information on what this method does
357     */
358    @NonNull
359    public ConfigurationOptions withShouldCopyDefaults(boolean shouldCopyDefaults) {
360        if (this.shouldCopyDefaults == shouldCopyDefaults) {
361            return this;
362        }
363        return new ConfigurationOptions(mapFactory, header, serializers, acceptedTypes, objectMapperFactory, shouldCopyDefaults);
364    }
365
366    @Override
367    public boolean equals(Object o) {
368        if (this == o) return true;
369        if (!(o instanceof ConfigurationOptions)) return false;
370        ConfigurationOptions that = (ConfigurationOptions) o;
371        return Objects.equals(shouldCopyDefaults, that.shouldCopyDefaults) &&
372                Objects.equals(mapFactory, that.mapFactory) &&
373                Objects.equals(header, that.header) &&
374                Objects.equals(serializers, that.serializers) &&
375                Objects.equals(acceptedTypes, that.acceptedTypes) &&
376                Objects.equals(objectMapperFactory, that.objectMapperFactory);
377    }
378
379    @Override
380    public int hashCode() {
381        return Objects.hash(mapFactory, header, serializers, acceptedTypes, objectMapperFactory, shouldCopyDefaults);
382    }
383
384    @Override
385    public String toString() {
386        return "ConfigurationOptions{" +
387                "mapFactory=" + mapFactory +
388                ", header='" + header + '\'' +
389                ", serializers=" + serializers +
390                ", acceptedTypes=" + acceptedTypes +
391                ", objectMapperFactory=" + objectMapperFactory +
392                ", shouldCopyDefaults=" + shouldCopyDefaults +
393                '}';
394    }
395}