package envoy.data; import java.io.*; import java.util.*; import java.util.function.Function; import java.util.logging.Level; import java.util.stream.Collectors; import envoy.util.EnvoyLog; /** * Manages all application settings that are set during application startup by either loading them * from the {@link Properties} file (default values) {@code client.properties} or parsing them from * the command line arguments of the application. *

* All items inside the {@code Config} are supposed to either be supplied over default value or over * command line argument. Developers that fail to provide default values will be greeted with an * error message the next time they try to start Envoy... * * @author Kai S. K. Engelbart * @since Envoy Common v0.1-beta */ public class Config { protected Map> items = new HashMap<>(); private boolean modificationDisabled; protected Config(String folderName) { final var rootDirectory = new File(System.getProperty("user.home"), folderName); put("homeDirectory", "home", File::new); ((ConfigItem) get("homeDirectory")).setValue(rootDirectory); put("fileLevelBarrier", "fb", Level::parse); put("consoleLevelBarrier", "cb", Level::parse); } /** * Parses config items from a properties object. * * @param properties the properties object to parse * @since Envoy Common v0.1-beta */ private void load(Properties properties) { items.entrySet().stream().filter(e -> properties.containsKey(e.getKey())) .forEach(e -> e.getValue().parse(properties.getProperty(e.getKey()))); } /** * Parses config items from an array of command line arguments. * * @param args the command line arguments to parse * @throws IllegalStateException if a malformed command line argument has been supplied * @since Envoy Common v0.1-beta */ private void load(String[] args) { for (int i = 0; i < args.length; i++) for (final ConfigItem item : items.values()) if (args[i].startsWith("--")) { if (args[i].length() == 2) throw new IllegalStateException( "Malformed command line argument at position " + i + ": " + args[i]); final String commandLong = args[i].substring(2); if (item.getCommandLong().equals(commandLong)) { item.parse(args[++i]); break; } } else if (args[i].startsWith("-")) { if (args[i].length() == 1) throw new IllegalStateException( "Malformed command line argument at position " + i + ": " + args[i]); final String commandShort = args[i].substring(1); if (item.getCommandShort().equals(commandShort)) { item.parse(args[++i]); break; } } else throw new IllegalStateException( "Malformed command line argument at position " + i + ": " + args[i]); } /** * Supplies default values from the given .properties file and parses the configuration from an * array of command line arguments. * * @param declaringClass the class calling this method * @param propertiesFilePath the path to where the .properties file can be found - will be only * the file name if it is located directly inside the * {@code src/main/resources} folder * @param args the command line arguments to parse * @throws IllegalStateException if this method is getting called again or if a malformed * command line argument has been supplied * @since Envoy Common v0.1-beta */ public void loadAll(Class declaringClass, String propertiesFilePath, String[] args) { if (modificationDisabled) throw new IllegalStateException( "Cannot change config after isInitialized has been called"); // Load the defaults from the given .properties file first final var properties = new Properties(); try { properties .load(declaringClass.getClassLoader().getResourceAsStream(propertiesFilePath)); } catch (final IOException e) { EnvoyLog.getLogger(Config.class).log(Level.SEVERE, "An error occurred when reading in the configuration: ", e); } load(properties); // Override configuration values with command line arguments if (args.length > 0) load(args); // Check if all configuration values have been initialized isInitialized(); // Disable further editing of the config modificationDisabled = true; } /** * @throws IllegalStateException if a {@link ConfigItem} has not been initialized * @since Envoy Common v0.1-beta */ private void isInitialized() { String uninitialized = items.values().stream().filter(c -> c.get() == null) .map(ConfigItem::getCommandLong).collect(Collectors.joining(", ")); if (!uninitialized.isEmpty()) throw new IllegalStateException("Config items uninitialized: " + uninitialized); } /** * @param name the name of the config item to return * @return the config item with the specified name * @since Envoy Common v0.1-beta */ public ConfigItem get(String name) { return items.get(name); } /** * Shorthand for
* {@code items.put(commandName, new ConfigItem<>(commandName, commandShort, parseFunction, defaultValue))}. * * @param the type of the {@link ConfigItem} * @param commandName the key for this config item as well as its long name * @param commandShort the abbreviation of this config item * @param parseFunction the {@code Function} that parses the value from a string * @since Envoy Common v0.2-beta */ protected void put(String commandName, String commandShort, Function parseFunction) { items.put(commandName, new ConfigItem<>(commandName, commandShort, parseFunction)); } /** * @return the directory in which all local files are saves * @since Envoy Client v0.2-beta */ public File getHomeDirectory() { return (File) items.get("homeDirectory").get(); } /** * @return the minimal {@link Level} to log inside the log file * @since Envoy Client v0.2-beta */ public Level getFileLevelBarrier() { return (Level) items.get("fileLevelBarrier").get(); } /** * @return the minimal {@link Level} to log inside the console * @since Envoy Client v0.2-beta */ public Level getConsoleLevelBarrier() { return (Level) items.get("consoleLevelBarrier").get(); } }