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.reference; 018 019import io.leangen.geantyref.TypeToken; 020import org.checkerframework.checker.nullness.qual.Nullable; 021import org.spongepowered.configurate.ConfigurateException; 022import org.spongepowered.configurate.ConfigurationNode; 023import org.spongepowered.configurate.NodePath; 024import org.spongepowered.configurate.ScopedConfigurationNode; 025import org.spongepowered.configurate.loader.ConfigurationLoader; 026import org.spongepowered.configurate.reactive.Publisher; 027import org.spongepowered.configurate.reactive.TransactionalSubscriber; 028import org.spongepowered.configurate.serialize.SerializationException; 029 030import java.nio.file.Path; 031import java.util.Map; 032import java.util.concurrent.ForkJoinPool; 033import java.util.function.Function; 034 035/** 036 * An updating reference to a base configuration node. 037 * 038 * @param <N> the type of node to work with 039 * @since 4.0.0 040 */ 041public interface ConfigurationReference<N extends ConfigurationNode> extends AutoCloseable { 042 043 /** 044 * Create a new configuration reference that will only update when loaded. 045 * 046 * @param loader the loader to load and save from 047 * @param <N> the type of node 048 * @return the newly created reference, with an initial load performed 049 * @throws ConfigurateException if the configuration contained fails to load 050 * @since 4.0.0 051 */ 052 static <N extends ScopedConfigurationNode<N>> ConfigurationReference<N> 053 fixed(final ConfigurationLoader<? extends N> loader) throws ConfigurateException { 054 final ConfigurationReference<N> ret = new ManualConfigurationReference<>(loader, ForkJoinPool.commonPool()); 055 ret.load(); 056 return ret; 057 } 058 059 /** 060 * Create a new configuration reference that will automatically update when 061 * triggered by the provided {@link WatchServiceListener}. 062 * 063 * @param loaderCreator a function that can create a {@link ConfigurationLoader} 064 * @param file the file to load this configuration from 065 * @param listener the watch service listener that will receive events 066 * @param <T> the node type 067 * @return the created reference 068 * @throws ConfigurateException if the underlying loader fails to load 069 * a configuration 070 * @see WatchServiceListener#listenToConfiguration(Function, Path) 071 * @since 4.0.0 072 */ 073 static <T extends ScopedConfigurationNode<T>> ConfigurationReference<T> 074 watching(final Function<Path, ConfigurationLoader<? extends T>> loaderCreator, final Path file, final WatchServiceListener listener) 075 throws ConfigurateException { 076 final WatchingConfigurationReference<T> ret = new WatchingConfigurationReference<>(loaderCreator.apply(file), listener.taskExecutor); 077 ret.load(); 078 ret.disposable(listener.listenToFile(file, ret)); 079 080 return ret; 081 } 082 083 /** 084 * Reload a configuration using the provided loader. 085 * 086 * <p>If the load fails, this reference will continue pointing to old 087 * configuration values. 088 * 089 * @throws ConfigurateException when an error occurs 090 * @since 4.0.0 091 */ 092 void load() throws ConfigurateException; 093 094 /** 095 * Save this configuration using the provided loader. 096 * 097 * @throws ConfigurateException when an error occurs in the underlying IO 098 * @since 4.0.0 099 */ 100 void save() throws ConfigurateException; 101 102 /** 103 * Update the configuration node pointed to by this reference, and save it 104 * using the reference's loader. 105 * 106 * <p>Even if the loader fails to save this new node, the node pointed to by 107 * this reference will be updated. 108 * 109 * @param newNode the new node to save 110 * @throws ConfigurateException when an error occurs within the loader 111 * @since 4.0.0 112 */ 113 void save(ConfigurationNode newNode) throws ConfigurateException; 114 115 /** 116 * Save this configuration using the provided loader. Any errors will be 117 * submitted to subscribers of the returned publisher. 118 * 119 * @return publisher providing an event when the save is complete 120 * @since 4.0.0 121 */ 122 Publisher<N> saveAsync(); 123 124 /** 125 * Update this configuration using the provided function, returning a 126 * {@link Publisher} which will complete with the result of the operation. 127 * The update function will be called asynchronously, and will be saved 128 * to this reference's loader when complete. 129 * 130 * @param updater update function 131 * @return publisher providing an event when the update is complete 132 * @since 4.0.0 133 */ 134 Publisher<N> updateAsync(Function<N, ? extends N> updater); 135 136 /** 137 * Get the base node this reference refers to. 138 * 139 * @return the node 140 * @since 4.0.0 141 */ 142 N node(); 143 144 /** 145 * Get the loader this reference uses to load and save its node. 146 * 147 * @return the loader 148 * @since 4.0.0 149 */ 150 ConfigurationLoader<? extends N> loader(); 151 152 /** 153 * Get the node at the given path, relative to the root node. 154 * 155 * @param path the path, a series of path elements 156 * @return a child node 157 * @see ConfigurationNode#node(Object...) 158 * @since 4.0.0 159 */ 160 N get(Object... path); 161 162 /** 163 * Get the node at the given path, relative to the root node. 164 * 165 * @param path the path, a series of path elements 166 * @return a child node 167 * @see ConfigurationNode#node(Iterable) 168 * @since 4.0.0 169 */ 170 N get(Iterable<?> path); 171 172 /** 173 * Update the value of the node at the given path, using the root node as 174 * a base. 175 * 176 * @param path the path to get the child at 177 * @param value the value to set the child node to 178 * @throws SerializationException when unable to write the provided value 179 * @since 4.0.0 180 */ 181 default void set(final Object[] path, final @Nullable Object value) throws SerializationException { 182 this.node().node(path).set(value); 183 } 184 185 /** 186 * Set the value of the node at {@code path} to the given value. 187 * 188 * <p>This uses the appropriate 189 * {@link org.spongepowered.configurate.serialize.TypeSerializer} to 190 * serialize the data if it's not directly supported by the 191 * provided configuration.</p> 192 * 193 * @param path the path to set the value at 194 * @param type the type of data to serialize 195 * @param value the value to set 196 * @param <T> the type parameter for the value 197 * @throws IllegalArgumentException if a raw type is provided 198 * @throws SerializationException if thrown by the serialization mechanism 199 * @since 4.0.0 200 */ 201 default <T> void set(final Object[] path, final Class<T> type, final @Nullable T value) throws SerializationException { 202 this.node().node(path).set(type, value); 203 } 204 205 /** 206 * Set the value of the node at {@code path} to the given value. 207 * 208 * <p>This uses the appropriate 209 * {@link org.spongepowered.configurate.serialize.TypeSerializer} to 210 * serialize the data if it's not directly supported by the 211 * provided configuration.</p> 212 * 213 * @param path the path to set the value at 214 * @param type the type of data to serialize 215 * @param value the value to set 216 * @param <T> the type parameter for the value 217 * @throws SerializationException if thrown by the serialization mechanism 218 * @since 4.0.0 219 */ 220 default <T> void set(final Object[] path, final TypeToken<T> type, final @Nullable T value) throws SerializationException { 221 this.node().node(path).set(type, value); 222 } 223 224 /** 225 * Update the value of the node at the given path, using the root node as 226 * a base. 227 * 228 * @param path the path to get the child at 229 * @param value the value to set the child node to 230 * @since 4.0.0 231 */ 232 default void set(final NodePath path, final @Nullable Object value) throws SerializationException { 233 this.node().node(path).set(value); 234 } 235 236 /** 237 * Set the value of the node at {@code path} to the given value. 238 * 239 * <p>This uses the appropriate 240 * {@link org.spongepowered.configurate.serialize.TypeSerializer} to 241 * serialize the data if it's not directly supported by the 242 * provided configuration.</p> 243 * 244 * @param path the path to set the value at 245 * @param type the type of data to serialize 246 * @param value the value to set 247 * @param <T> the type parameter for the value 248 * @throws SerializationException if thrown by the serialization mechanism 249 * @since 4.0.0 250 */ 251 default <T> void set(final NodePath path, final Class<T> type, final @Nullable T value) throws SerializationException { 252 this.node().node(path).set(type, value); 253 } 254 255 /** 256 * Set the value of the node at {@code path} to the given value. 257 * 258 * <p>This uses the appropriate 259 * {@link org.spongepowered.configurate.serialize.TypeSerializer} to 260 * serialize the data if it's not directly supported by the 261 * provided configuration.</p> 262 * 263 * @param path the path to set the value at 264 * @param type the type of data to serialize 265 * @param value the value to set 266 * @param <T> the type parameter for the value 267 * @throws SerializationException if thrown by the serialization mechanism 268 * @since 4.0.0 269 */ 270 default <T> void set(final NodePath path, final TypeToken<T> type, final @Nullable T value) throws SerializationException { 271 this.node().node(path).set(type, value); 272 } 273 274 /** 275 * Create a reference to the node at the provided path. The value will be 276 * deserialized according to the provided TypeToken. 277 * 278 * <p>The returned reference will update with reloads of and changes to the 279 * value of the provided configuration. Any serialization errors encountered 280 * will be submitted to the {@link #errors()} stream. 281 * 282 * @param type the value's type 283 * @param path the path from the root node to the node containing the value 284 * @param <T> the value type 285 * @return a deserializing reference to the node at the given path 286 * @throws SerializationException if a type serializer could not be found 287 * for the provided type 288 * @since 4.0.0 289 */ 290 default <T> ValueReference<T, N> referenceTo(final TypeToken<T> type, final Object... path) throws SerializationException { 291 return this.referenceTo(type, NodePath.of(path)); 292 } 293 294 /** 295 * Create a reference to the node at the provided path. The value will be 296 * deserialized according to type of the provided {@link Class}. 297 * 298 * <p>The returned reference will update with reloads of and changes to the 299 * value of the provided configuration. Any serialization errors encountered 300 * will be submitted to the {@link #errors()} stream. 301 * 302 * @param type the value's type 303 * @param path the path from the root node to the node containing the value 304 * @param <T> the value type 305 * @return a deserializing reference to the node at the given path 306 * @throws SerializationException if a type serializer could not be found 307 * for the provided type 308 * @since 4.0.0 309 */ 310 default <T> ValueReference<T, N> referenceTo(final Class<T> type, final Object... path) throws SerializationException { 311 return this.referenceTo(type, NodePath.of(path)); 312 } 313 314 /** 315 * Create a reference to the node at the provided path. The value will be 316 * deserialized according to the provided {@link TypeToken}. 317 * 318 * <p>The returned reference will update with reloads of and changes to the 319 * value of the provided configuration. Any serialization errors encountered 320 * will be submitted to the {@link #errors()} stream. 321 * 322 * @param type the value's type 323 * @param path the path from the root node to the node containing the value 324 * @param <T> the value type 325 * @return a deserializing reference to the node at the given path 326 * @throws SerializationException if a type serializer could not be found 327 * for the provided type 328 * @since 4.0.0 329 */ 330 default <T> ValueReference<T, N> referenceTo(final TypeToken<T> type, final NodePath path) throws SerializationException { 331 return this.referenceTo(type, path, null); 332 } 333 334 /** 335 * Create a reference to the node at the provided path. The value will be 336 * deserialized according to type of the provided {@link Class}. 337 * 338 * <p>The returned reference will update with reloads of and changes to the 339 * value of the provided configuration. Any serialization errors encountered 340 * will be submitted to the {@link #errors()} stream. 341 * 342 * @param type the value's type 343 * @param path the path from the root node to the node containing the value 344 * @param <T> the value type 345 * @return a deserializing reference to the node at the given path 346 * @throws SerializationException if a type serializer could not be found 347 * for the provided type 348 * @since 4.0.0 349 */ 350 default <T> ValueReference<T, N> referenceTo(final Class<T> type, final NodePath path) throws SerializationException { 351 return this.referenceTo(type, path, null); 352 } 353 354 /** 355 * Create a reference to the node at the provided path. The value will be 356 * deserialized according to the provided {@link TypeToken}. 357 * 358 * <p>The returned reference will update with reloads of and changes to the 359 * value of the provided configuration. Any serialization errors encountered 360 * will be submitted to the {@link #errors()} stream. 361 * 362 * @param type the value's type. 363 * @param path the path from the root node to the node containing the value 364 * @param defaultValue the value to use when there is no data present 365 * in the targeted node. 366 * @param <T> the value type 367 * @return a deserializing reference to the node at the given path 368 * @throws SerializationException if a type serializer could not be found 369 * for the provided type 370 * @since 4.0.0 371 */ 372 <T> ValueReference<T, N> referenceTo(TypeToken<T> type, NodePath path, @Nullable T defaultValue) throws SerializationException; 373 374 /** 375 * Create a reference to the node at the provided path. The value will be 376 * deserialized according to type of the provided {@link Class}. 377 * 378 * <p>The returned reference will update with reloads of and changes to the 379 * value of the provided configuration. Any serialization errors encountered 380 * will be submitted to the {@link #errors()} stream. 381 * 382 * @param type value's type 383 * @param path path from the root node to the node containing the value 384 * @param defaultValue value to use when there is no data present 385 * in the targeted node. 386 * @param <T> value type 387 * @return a deserializing reference to the node at the given path 388 * @throws SerializationException if a type serializer could not be found 389 * for the provided type 390 * @since 4.0.0 391 */ 392 <T> ValueReference<T, N> referenceTo(Class<T> type, NodePath path, @Nullable T defaultValue) throws SerializationException; 393 394 /** 395 * Access the {@link Publisher} that will broadcast update events, providing the newly created node. The returned 396 * publisher will be transaction-aware, i.e. any {@link TransactionalSubscriber} attached will progress through 397 * their phases appropriately 398 * 399 * @return the publisher 400 * @since 4.0.0 401 */ 402 Publisher<N> updates(); 403 404 /** 405 * A stream that will receive errors that occur while loading or saving to 406 * this reference. 407 * 408 * @return the publisher 409 * @since 4.0.0 410 */ 411 Publisher<Map.Entry<ErrorPhase, Throwable>> errors(); 412 413 /** 414 * {@inheritDoc} 415 */ 416 @Override 417 void close(); 418 419 /** 420 * Representing the phase where an error occurred. 421 * 422 * @since 4.0.0 423 */ 424 enum ErrorPhase { 425 /** 426 * While loading a configuration node from an external location. 427 * 428 * @since 4.0.0 429 */ 430 LOADING, 431 /** 432 * While saving a configuration node to an external location. 433 * 434 * @since 4.0.0 435 */ 436 SAVING, 437 /** 438 * When no further detail is known. 439 * 440 * @since 4.0.0 441 */ 442 UNKNOWN, 443 /** 444 * While deserializing a value. 445 * 446 * @since 4.0.0 447 */ 448 VALUE; 449 450 } 451 452}