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