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}