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(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(Function<Path, ConfigurationLoader<? extends T>> loaderCreator, Path file, 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     * @since 4.0.0
179     */
180    default void set(Object[] path, @Nullable Object value) throws SerializationException {
181        node().node(path).set(value);
182    }
183
184    /**
185     * Set the value of the node at {@code path} to the given value.
186     *
187     * <p>This uses the appropriate
188     * {@link org.spongepowered.configurate.serialize.TypeSerializer} to
189     * serialize the data if it's not directly supported by the
190     * provided configuration.</p>
191     *
192     * @param path the path to set the value at
193     * @param type the type of data to serialize
194     * @param value the value to set
195     * @param <T> the type parameter for the value
196     * @throws IllegalArgumentException if a raw type is provided
197     * @throws SerializationException if thrown by the serialization mechanism
198     * @since 4.0.0
199     */
200    default <T> void set(Object[] path, Class<T> type, @Nullable T value) throws SerializationException {
201        node().node(path).set(type, value);
202    }
203
204    /**
205     * Set the value of the node at {@code path} to the given value.
206     *
207     * <p>This uses the appropriate
208     * {@link org.spongepowered.configurate.serialize.TypeSerializer} to
209     * serialize the data if it's not directly supported by the
210     * provided configuration.</p>
211     *
212     * @param path the path to set the value at
213     * @param type the type of data to serialize
214     * @param value the value to set
215     * @param <T> the type parameter for the value
216     * @throws SerializationException if thrown by the serialization mechanism
217     * @since 4.0.0
218     */
219    default <T> void set(Object[] path, TypeToken<T> type, @Nullable T value) throws SerializationException {
220        node().node(path).set(type, value);
221    }
222
223    /**
224     * Update the value of the node at the given path, using the root node as
225     * a base.
226     *
227     * @param path the path to get the child at
228     * @param value the value to set the child node to
229     * @since 4.0.0
230     */
231    default void set(NodePath path, @Nullable Object value) throws SerializationException {
232        node().node(path).set(value);
233    }
234
235    /**
236     * Set the value of the node at {@code path} to the given value.
237     *
238     * <p>This uses the appropriate
239     * {@link org.spongepowered.configurate.serialize.TypeSerializer} to
240     * serialize the data if it's not directly supported by the
241     * provided configuration.</p>
242     *
243     * @param path the path to set the value at
244     * @param type the type of data to serialize
245     * @param value the value to set
246     * @param <T> the type parameter for the value
247     * @throws SerializationException if thrown by the serialization mechanism
248     * @since 4.0.0
249     */
250    default <T> void set(NodePath path, Class<T> type, @Nullable T value) throws SerializationException {
251        node().node(path).set(type, value);
252    }
253
254    /**
255     * Set the value of the node at {@code path} to the given value.
256     *
257     * <p>This uses the appropriate
258     * {@link org.spongepowered.configurate.serialize.TypeSerializer} to
259     * serialize the data if it's not directly supported by the
260     * provided configuration.</p>
261     *
262     * @param path the path to set the value at
263     * @param type the type of data to serialize
264     * @param value the value to set
265     * @param <T> the type parameter for the value
266     * @throws SerializationException if thrown by the serialization mechanism
267     * @since 4.0.0
268     */
269    default <T> void set(NodePath path, TypeToken<T> type, @Nullable T value) throws SerializationException {
270        node().node(path).set(type, value);
271    }
272
273    /**
274     * Create a reference to the node at the provided path. The value will be
275     * deserialized according to the provided TypeToken.
276     *
277     * <p>The returned reference will update with reloads of and changes to the
278     * value of the provided configuration. Any serialization errors encountered
279     * will be submitted to the {@link #errors()} stream.
280     *
281     * @param type the value's type
282     * @param path the path from the root node to the node containing the value
283     * @param <T>  the value type
284     * @return a deserializing reference to the node at the given path
285     * @throws SerializationException if a type serializer could not be found
286     *         for the provided type
287     * @since 4.0.0
288     */
289    default <T> ValueReference<T, N> referenceTo(TypeToken<T> type, Object... path) throws SerializationException {
290        return referenceTo(type, NodePath.of(path));
291    }
292
293    /**
294     * Create a reference to the node at the provided path. The value will be
295     * deserialized according to type of the provided {@link Class}.
296     *
297     * <p>The returned reference will update with reloads of and changes to the
298     * value of the provided configuration. Any serialization errors encountered
299     * will be submitted to the {@link #errors()} stream.
300     *
301     * @param type the value's type
302     * @param path the path from the root node to the node containing the value
303     * @param <T>  the value type
304     * @return a deserializing reference to the node at the given path
305     * @throws SerializationException if a type serializer could not be found
306     *         for the provided type
307     * @since 4.0.0
308     */
309    default <T> ValueReference<T, N> referenceTo(Class<T> type, Object... path) throws SerializationException {
310        return referenceTo(type, NodePath.of(path));
311    }
312
313    /**
314     * Create a reference to the node at the provided path. The value will be
315     * deserialized according to the provided {@link TypeToken}.
316     *
317     * <p>The returned reference will update with reloads of and changes to the
318     * value of the provided configuration. Any serialization errors encountered
319     * will be submitted to the {@link #errors()} stream.
320     *
321     * @param type the value's type
322     * @param path the path from the root node to the node containing the value
323     * @param <T> the value type
324     * @return a deserializing reference to the node at the given path
325     * @throws SerializationException if a type serializer could not be found
326     *          for the provided type
327     * @since 4.0.0
328     */
329    default <T> ValueReference<T, N> referenceTo(TypeToken<T> type, NodePath path) throws SerializationException {
330        return referenceTo(type, path, null);
331    }
332
333    /**
334     * Create a reference to the node at the provided path. The value will be
335     * deserialized according to type of the provided {@link Class}.
336     *
337     * <p>The returned reference will update with reloads of and changes to the
338     * value of the provided configuration. Any serialization errors encountered
339     * will be submitted to the {@link #errors()} stream.
340     *
341     * @param type the value's type
342     * @param path the path from the root node to the node containing the value
343     * @param <T> the value type
344     * @return a deserializing reference to the node at the given path
345     * @throws SerializationException if a type serializer could not be found
346     *          for the provided type
347     * @since 4.0.0
348     */
349    default <T> ValueReference<T, N> referenceTo(Class<T> type, NodePath path) throws SerializationException {
350        return referenceTo(type, path, null);
351    }
352
353    /**
354     * Create a reference to the node at the provided path. The value will be
355     * deserialized according to the provided {@link TypeToken}.
356     *
357     * <p>The returned reference will update with reloads of and changes to the
358     * value of the provided configuration. Any serialization errors encountered
359     * will be submitted to the {@link #errors()} stream.
360     *
361     * @param type the value's type.
362     * @param path the path from the root node to the node containing the value
363     * @param defaultValue the value to use when there is no data present
364     *                     in the targeted node.
365     * @param <T> the value type
366     * @return a deserializing reference to the node at the given path
367     * @throws SerializationException if a type serializer could not be found
368     *         for the provided type
369     * @since 4.0.0
370     */
371    <T> ValueReference<T, N> referenceTo(TypeToken<T> type, NodePath path, @Nullable T defaultValue) throws SerializationException;
372
373    /**
374     * Create a reference to the node at the provided path. The value will be
375     * deserialized according to type of the provided {@link Class}.
376     *
377     * <p>The returned reference will update with reloads of and changes to the
378     * value of the provided configuration. Any serialization errors encountered
379     * will be submitted to the {@link #errors()} stream.
380     *
381     * @param type value's type
382     * @param path path from the root node to the node containing the value
383     * @param defaultValue value to use when there is no data present
384     *                     in the targeted node.
385     * @param <T> value type
386     * @return a deserializing reference to the node at the given path
387     * @throws SerializationException if a type serializer could not be found
388     *          for the provided type
389     * @since 4.0.0
390     */
391    <T> ValueReference<T, N> referenceTo(Class<T> type, NodePath path, @Nullable T defaultValue) throws SerializationException;
392
393    /**
394     * Access the {@link Publisher} that will broadcast update events, providing the newly created node. The returned
395     * publisher will be transaction-aware, i.e. any {@link TransactionalSubscriber} attached will progress through
396     * their phases appropriately
397     *
398     * @return the publisher
399     * @since 4.0.0
400     */
401    Publisher<N> updates();
402
403    /**
404     * A stream that will receive errors that occur while loading or saving to
405     * this reference.
406     *
407     * @return the publisher
408     * @since 4.0.0
409     */
410    Publisher<Map.Entry<ErrorPhase, Throwable>> errors();
411
412    /**
413     * {@inheritDoc}
414     */
415    @Override
416    void close();
417
418    /**
419     * Representing the phase where an error occurred.
420     *
421     * @since 4.0.0
422     */
423    enum ErrorPhase {
424        LOADING, SAVING, UNKNOWN, VALUE;
425
426    }
427
428}