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.loader; 018 019import static java.util.Objects.requireNonNull; 020 021import org.checkerframework.checker.nullness.qual.Nullable; 022import org.spongepowered.configurate.ConfigurationNode; 023import org.spongepowered.configurate.serialize.Scalars; 024import org.spongepowered.configurate.serialize.SerializationException; 025 026import java.util.Arrays; 027 028/** 029 * A provider for {@link ConfigurationLoader} options. 030 * 031 * <p>This allows initializing a loader from sources like system properties, 032 * environment variables, or an existing configuration node.</p> 033 * 034 * <p>To allow working with primitive value sources, only 035 * scalar values are supported.</p> 036 * 037 * @since 4.2.0 038 */ 039@SuppressWarnings("checkstyle:NoGetSetPrefix") // we need something to not overlap with primitives 040public interface LoaderOptionSource { 041 042 /** 043 * Retrieve loader options from environment variables. 044 * 045 * <p>When reading environment variables, property keys will be converted to 046 * upper case and joined by '{@code _}' (an underscore).</p> 047 * 048 * <p>This source will use a default prefix of {@code CONFIGURATE}.</p> 049 * 050 * @return a source retrieving options from environment variables 051 * @since 4.2.0 052 */ 053 static LoaderOptionSource environmentVariables() { 054 return LoaderOptionSources.ENVIRONMENT; 055 } 056 057 /** 058 * Retrieve loader options from environment variables. 059 * 060 * <p>When reading environment variables, property keys will be converted to 061 * upper case and joined by '{@code _}' (an underscore).</p> 062 * 063 * @param prefix the prefix to prepend to option paths 064 * @return a source retrieving options from environment variables 065 * @since 4.2.0 066 */ 067 static LoaderOptionSource environmentVariables(final String prefix) { 068 return new LoaderOptionSources.EnvironmentVariables(requireNonNull(prefix, "prefix")); 069 } 070 071 /** 072 * Retrieve loader options from system properties. 073 * 074 * <p>When reading system properties, property keys will be joined by 075 * '{@code .}' (a dot).</p> 076 * 077 * <p>This source will use a default prefix of {@code configurate}.</p> 078 * 079 * @return a source retrieving options from system properties 080 * @since 4.2.0 081 */ 082 static LoaderOptionSource systemProperties() { 083 return LoaderOptionSources.SYSTEM_PROPERTIES; 084 } 085 086 /** 087 * Retrieve loader options from system properties. 088 * 089 * <p>When reading system properties, property keys will be joined by 090 * '{@code .}' (a dot).</p> 091 * 092 * @param prefix the prefix to prepend to option paths 093 * @return a source retrieving options from system properties 094 * @since 4.2.0 095 */ 096 static LoaderOptionSource systemProperties(final String prefix) { 097 return new LoaderOptionSources.SystemProperties(requireNonNull(prefix, "prefix")); 098 } 099 100 /** 101 * Create an option source that will read from an existing 102 * configuration node. 103 * 104 * <p>Option paths will be converted directly to node paths.</p> 105 * 106 * @param node the node to read options from 107 * @return a source retrieving options from the provided node 108 * @since 4.2.0 109 */ 110 static LoaderOptionSource node(final ConfigurationNode node) { 111 return new LoaderOptionSources.Node(requireNonNull(node, "node")); 112 } 113 114 /** 115 * Create an option source that will try the provided sources in order. 116 * 117 * <p>The first source with a present value will be used.</p> 118 * 119 * @param sources the option sources to delegate to 120 * @return a new option source 121 * @since 4.2.0 122 */ 123 static LoaderOptionSource composite(final LoaderOptionSource... sources) { 124 return new LoaderOptionSources.Composite(Arrays.copyOf(sources, sources.length)); 125 } 126 127 /** 128 * Get the value at the provided path. 129 * 130 * @param path the path for options. 131 * @return a value, or {@code null} if none is present 132 * @throws IllegalArgumentException if the provided path is an empty array 133 * @since 4.2.0 134 */ 135 @Nullable String get(String... path); 136 137 /** 138 * Get the value at the provided path. 139 * 140 * <p>If no value is present, {@code defaultValue} will be returned.</p> 141 * 142 * @param path the path for options. 143 * @param defaultValue the value to return if none is present 144 * @return a value, or {@code defaultValue} if none is present 145 * @throws IllegalArgumentException if the provided path is an empty array 146 * @since 4.2.0 147 */ 148 default String getOr(final String defaultValue, final String... path) { 149 final @Nullable String value = this.get(path); 150 return value == null ? defaultValue : value; 151 } 152 153 /** 154 * Get the value at the provided path as an enum constant. 155 * 156 * @param <T> enum type 157 * @param enumClazz the enum type's class 158 * @param path the path for options. 159 * @return a value, or {@code null} if none is present or the provided value 160 * is not a member of the enum 161 * @throws IllegalArgumentException if the provided path is an empty array 162 * @since 4.2.0 163 */ 164 @SuppressWarnings("unchecked") 165 default <T extends Enum<T>> @Nullable T getEnum(final Class<T> enumClazz, final String... path) { 166 final @Nullable String value = this.get(path); 167 try { 168 return value == null ? null : (T) Scalars.ENUM.deserialize(enumClazz, value); 169 } catch (SerializationException e) { 170 return null; 171 } 172 } 173 174 /** 175 * Get the value at the provided path as an enum constant. 176 * 177 * <p>If no value is present or the provided value is not a known enum 178 * constant, {@code defaultValue} will be returned.</p> 179 * 180 * @param <T> enum type 181 * @param enumClazz the enum type's class 182 * @param path the path for options. 183 * @param defaultValue the value to return if none is present 184 * @return a value, or {@code defaultValue} if none is present 185 * @throws IllegalArgumentException if the provided path is an empty array 186 * @since 4.2.0 187 */ 188 default <T extends Enum<T>> T getEnum(final Class<T> enumClazz, final T defaultValue, final String... path) { 189 final @Nullable T value = this.getEnum(enumClazz, path); 190 return value == null ? defaultValue : value; 191 } 192 193 // For primitives, a default is required since there is no null value 194 195 /** 196 * Get the value at the provided path as an integer. 197 * 198 * <p>If no value is present or the value is not a valid integer, 199 * {@code defaultValue} will be returned.</p> 200 * 201 * @param path the path for options. 202 * @param defaultValue the value to return if none is available 203 * @return a value, or {@code defaultValue} if none is present 204 * @throws IllegalArgumentException if the provided path is an empty array 205 * @since 4.2.0 206 */ 207 default int getInt(final int defaultValue, final String... path) { 208 final @Nullable String value = this.get(path); 209 if (value == null) { 210 return defaultValue; 211 } 212 final @Nullable Integer attempt = Scalars.INTEGER.tryDeserialize(value); 213 return attempt == null ? defaultValue : attempt; 214 } 215 216 /** 217 * Get the value at the provided path as a double. 218 * 219 * <p>If no value is present or the value is not a valid double, 220 * {@code defaultValue} will be returned.</p> 221 * 222 * @param path the path for options. 223 * @param defaultValue the value to return if none is available 224 * @return a value, or {@code defaultValue} if none is present 225 * @throws IllegalArgumentException if the provided path is an empty array 226 * @since 4.2.0 227 */ 228 default double getDouble(final double defaultValue, final String... path) { 229 final @Nullable String value = this.get(path); 230 if (value == null) { 231 return defaultValue; 232 } 233 final @Nullable Double attempt = Scalars.DOUBLE.tryDeserialize(value); 234 return attempt == null ? defaultValue : attempt; 235 } 236 237 /** 238 * Get the value at the provided path as a boolean. 239 * 240 * <p>If no value is present or the value is not a valid boolean, 241 * {@code defaultValue} will be returned.</p> 242 * 243 * @param path the path for options. 244 * @param defaultValue the value to return if none is available 245 * @return a value, or {@code defaultValue} if none is present 246 * @throws IllegalArgumentException if the provided path is an empty array 247 * @since 4.2.0 248 */ 249 default boolean getBoolean(final boolean defaultValue, final String... path) { 250 final @Nullable String value = this.get(path); 251 if (value == null) { 252 return defaultValue; 253 } 254 final @Nullable Boolean attempt = Scalars.BOOLEAN.tryDeserialize(value); 255 return attempt == null ? defaultValue : attempt; 256 } 257 258}