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.reference; 018 019import com.google.common.reflect.TypeToken; 020import ninja.leaping.configurate.ConfigurationNode; 021import ninja.leaping.configurate.loader.ConfigurationLoader; 022import ninja.leaping.configurate.objectmapping.ObjectMappingException; 023import ninja.leaping.configurate.reactive.Publisher; 024import ninja.leaping.configurate.reactive.TransactionalSubscriber; 025import org.checkerframework.checker.nullness.qual.Nullable; 026 027import java.io.IOException; 028import java.nio.file.Path; 029import java.util.Map; 030import java.util.concurrent.ForkJoinPool; 031import java.util.function.Function; 032 033/** 034 * An updating reference to a base configuration node 035 * 036 * @param <N> The type of node to work with 037 */ 038public interface ConfigurationReference<N extends ConfigurationNode> extends AutoCloseable { 039 /** 040 * Create a new configuration reference that will only update when loaded 041 * 042 * @param loader The loader to load and save from 043 * @param <N> The type of node 044 * @return The newly created reference, with an initial load performed 045 * @throws IOException If the configuration contained fails to load 046 */ 047 static <N extends ConfigurationNode> ConfigurationReference<N> createFixed(ConfigurationLoader<? extends N> loader) throws IOException { 048 ConfigurationReference<N> ret = new ManualConfigurationReference<>(loader, ForkJoinPool.commonPool()); 049 ret.load(); 050 return ret; 051 } 052 053 /** 054 * Create a new configuration reference that will automatically update when triggered by the provided {@link 055 * WatchServiceListener} 056 * 057 * @param loaderCreator A function that can create a {@link ConfigurationLoader} 058 * @param file The file to load this configuration from 059 * @param listener The watch service listener that will receive events 060 * @param <T> The node type 061 * @return The created reference 062 * @throws IOException If the underlying loader fails to load a configuration 063 * @see WatchServiceListener#listenToConfiguration(Function, Path) 064 */ 065 static <T extends ConfigurationNode> ConfigurationReference<T> createWatching(Function<Path, ConfigurationLoader<? extends T>> loaderCreator, Path file, WatchServiceListener listener) throws IOException { 066 final WatchingConfigurationReference<T> ret = new WatchingConfigurationReference<>(loaderCreator.apply(file), listener.taskExecutor); 067 ret.load(); 068 ret.setDisposable(listener.listenToFile(file, ret)); 069 070 return ret; 071 } 072 073 /** 074 * Reload a configuration using the provided loader. 075 * <p> 076 * If the load fails, this reference will continue pointing to old configuration values 077 * 078 * @throws IOException When an error occurs 079 */ 080 void load() throws IOException; 081 082 /** 083 * Save this configuration using the provided loader. 084 * 085 * @throws IOException When an error occurs in the underlying ID 086 */ 087 void save() throws IOException; 088 089 /** 090 * Update the configuration node pointed to by this reference, and save it using the reference's loader 091 * <p> 092 * Even if the loader fails to save this new node, the node pointed to by this reference will be updated. 093 * 094 * @param newNode The new node to save 095 * @throws IOException When an error occurs within the loader 096 */ 097 void save(N newNode) throws IOException; 098 099 /** 100 * Save this configuration using the provided loader. Any errors will be submitted to subscribers of the returned 101 * publisher. 102 * 103 * @return publisher providing an event when the save is complete 104 */ 105 Publisher<N> saveAsync(); 106 107 /** 108 * Update this configuration using the provided function, returning a {@link Publisher} which will complete with the 109 * result of the operation. The update function will be called asynchronously, and will be saved to this 110 * reference's loader when complete. 111 * 112 * @param updater update function 113 * @return publisher providing an event when the update is complete 114 */ 115 Publisher<N> updateAsync(Function<N, ? extends N> updater); 116 117 /** 118 * Get the base node this reference refers to. 119 * 120 * @return The node 121 */ 122 N getNode(); 123 124 /** 125 * Get the loader this reference uses to load and save its node 126 * 127 * @return The loader 128 */ 129 ConfigurationLoader<? extends N> getLoader(); 130 131 /** 132 * Get the node at the given path, using the root node 133 * 134 * @param path The path, a series of path elements 135 * @return A child node 136 * @see ConfigurationNode#getNode(Object...) 137 */ 138 N get(Object... path); 139 140 /** 141 * Update the value of the node at the given path, using the root node as a base. 142 * 143 * @param path The path to get the child at 144 * @param value The value to set the child node to 145 */ 146 default void set(Object[] path, @Nullable Object value) { 147 getNode().getNode(path).setValue(value); 148 } 149 150 /** 151 * Set the value of the node at {@code path} to the given value, using the appropriate {@link 152 * ninja.leaping.configurate.objectmapping.serialize.TypeSerializer} to serialize the data if it's not directly 153 * supported by the provided configuration. 154 * 155 * @param path The path to set the value at 156 * @param type The type of data to serialize 157 * @param value The value to set 158 * @param <T> The type parameter for the value 159 * @throws ObjectMappingException If thrown by the serialization mechanism 160 */ 161 default <T> void set(Object[] path, TypeToken<T> type, @Nullable T value) throws ObjectMappingException { 162 getNode().getNode(path).setValue(type, value); 163 } 164 165 /** 166 * Create a reference to the node at the provided path. The value will be deserialized according to the provided 167 * TypeToken. 168 * <p> 169 * The returned reference will update with reloads of and changes to the value of the provided configuration. Any 170 * serialization errors encountered will be submitted to the {@link #errors()} stream 171 * 172 * @param type The value's type 173 * @param path The path from the root node to the node a value will be gotten from 174 * @param <T> The value type 175 * @return A deserializing reference to the node at the given path 176 * @throws ObjectMappingException if a type serializer could not be found for the provided type 177 */ 178 default <T> ValueReference<T> referenceTo(TypeToken<T> type, Object... path) throws ObjectMappingException { 179 return referenceTo(type, path, null); 180 } 181 182 /** 183 * Create a reference to the node at the provided path. The value will be deserialized according to type of the 184 * provided Class. 185 * <p> 186 * The returned reference will update with reloads of and changes to the value of the provided configuration. Any 187 * serialization errors encountered will be submitted to the {@link #errors()} stream 188 * 189 * @param type The value's type 190 * @param path The path from the root node to the node a value will be gotten from 191 * @param <T> The value type 192 * @return A deserializing reference to the node at the given path 193 * @throws ObjectMappingException if a type serializer could not be found for the provided type 194 */ 195 default <T> ValueReference<T> referenceTo(Class<T> type, Object... path) throws ObjectMappingException { 196 return referenceTo(type, path, null); 197 } 198 199 /** 200 * Create a reference to the node at the provided path. The value will be deserialized according to the provided 201 * TypeToken. 202 * <p> 203 * The returned reference will update with reloads of and changes to the value of the provided configuration. Any 204 * serialization errors encountered will be submitted to the {@link #errors()} stream 205 * 206 * @param type The value's type. 207 * @param path The path from the root node to the node a value will be gotten from. 208 * @param defaultValue The value to use when there is no data present in the targeted node. 209 * @param <T> The value type 210 * @return A deserializing reference to the node at the given path 211 * @throws ObjectMappingException if a type serializer could not be found for the provided type 212 */ 213 <T> ValueReference<T> referenceTo(TypeToken<T> type, Object[] path, @Nullable T defaultValue) throws ObjectMappingException; 214 215 /** 216 * Create a reference to the node at the provided path. The value will be deserialized according to type of the 217 * provided Class. 218 * <p> 219 * The returned reference will update with reloads of and changes to the value of the provided configuration. Any 220 * serialization errors encountered will be submitted to the {@link #errors()} stream 221 * 222 * @param type The value's type 223 * @param path The path from the root node to the node a value will be gotten from 224 * @param defaultValue The value to use when there is no data present in the targeted node. 225 * @param <T> The value type 226 * @return A deserializing reference to the node at the given path 227 * @throws ObjectMappingException if a type serializer could not be found for the provided type 228 */ 229 <T> ValueReference<T> referenceTo(Class<T> type, Object[] path, @Nullable T defaultValue) throws ObjectMappingException; 230 231 /** 232 * Access the {@link Publisher} that will broadcast update events, providing the newly created node. The returned 233 * publisher will be transaction-aware, i.e. any {@link TransactionalSubscriber} attached will progress through 234 * their phases appropriately 235 * 236 * @return The publisher 237 */ 238 Publisher<N> updates(); 239 240 /** 241 * A stream that will receive errors that occur while loading or saving to this reference 242 * 243 * @return The publisher 244 */ 245 Publisher<Map.Entry<ErrorPhase, Throwable>> errors(); 246 247 /** 248 * {@inheritDoc} 249 */ 250 @Override 251 void close(); 252 253 /** 254 * Representing the phase where an error occurred 255 */ 256 enum ErrorPhase { 257 LOADING, SAVING, UNKNOWN, VALUE; 258 } 259}