All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.freeutils.util.config.Configuration Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright © 2003-2024 Amichai Rothman
 *
 *  This file is part of JElementary - the Java Elementary Utilities package.
 *
 *  JElementary is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  JElementary is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with JElementary.  If not, see .
 *
 *  For additional info see https://www.freeutils.net/source/jelementary/
 */

package net.freeutils.util.config;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.freeutils.util.Containers;
import net.freeutils.util.Strings;
import net.freeutils.util.Utils;

/**
 * The {@code Configuration} class encapsulates a configuration section.
 * 

* Each configuration section consists of various (ordered) configuration * items, each with a name and one or more values. A value can be either a * String or a nested Configuration section. *

* This forms a tree hierarchy similar to XML, but with no * dependency on a particular persistent format or implementation. */ public class Configuration { /** * An empty configuration instance which can be used as a configuration placeholder. */ public static Configuration EMPTY = new Configuration("EMPTY"); protected String name; protected final Map items = new LinkedHashMap<>(); /** * Constructs a Configuration section instance. This constructor can be * called only from subclasses, for populating the configuration contents. *

* Note that you can easily use an anonymous subclass to create an in-code * Configuration instance, for example: * * Configuration conf = new Configuration() {{ * add(param1, value1); * // additional configuration items... * }}; * * * @param name the name of this configuration section */ protected Configuration(String name) { this.name = name; } /** * Constructs a Configuration section instance. This constructor can be * called only from subclasses, for populating the configuration contents. */ protected Configuration() {} /** * Returns the name of this configuration section * * @return the name of this configuration section */ public String getName() { return name; } /** * Substitutes variables within the configuration values according * to the given substitution map. *

* Variables within configuration values are of the form ${NAME}, * and are replaced by the corresponding value, if it exists * (otherwise the variable literal is preserved). * * @param variables the substitution variables name-value map */ public void substituteVariables(Map variables) { Pattern pattern = Pattern.compile("\\$\\{([^}]*)\\}"); for (Map.Entry e : items.entrySet()) { Object item = e.getValue(); if (item instanceof String[]) { String[] strings = (String[])item; for (int i = 0; i < strings.length; i++) { if (strings[i].contains("${")) { // replace all variables in string Matcher matcher = pattern.matcher(strings[i]); StringBuffer sb = new StringBuffer(strings[i].length()); while (matcher.find()) { String name = matcher.group(1); String val = Utils.def(variables.get(name), matcher.group(0)); matcher.appendReplacement(sb, Strings.escape(val, "$\\")); } matcher.appendTail(sb); strings[i] = sb.toString(); } } } else if (item instanceof Configuration[]) { for (Configuration c : (Configuration[])item) c.substituteVariables(variables); } } } /** * Sets an item with the given name and configuration section values. * If an item with the same name already exists, it is overwritten. * * @param name the item's name * @param values the configuration section values * @throws NullPointerException if name or values is null */ protected void set(String name, Configuration... values) { if (name == null || values == null) throw new NullPointerException(); if (values.length > 0) // invariant: values are never empty items.put(name, values); } /** * Sets an item with the given name and string values. * If an item with the same name already exists, it is overwritten. * * @param name the item's name * @param values the string values * @throws NullPointerException if name or values is null */ protected void set(String name, String... values) { if (name == null || values == null) throw new NullPointerException(); if (values.length > 0) // invariant: values are never empty items.put(name, values); } /** * Adds the given configuration section values to the item with the given name. * If an item with the given name does not exist, it is created. * * @param name the item's name * @param values the configuration section values * @throws NullPointerException if name or values is null * @throws ConfigurationException if an item with the given name already exists, * but its value is of a different type */ protected void add(String name, Configuration... values) throws ConfigurationException { if (name == null || values == null) throw new NullPointerException(); try { Configuration[] v = (Configuration[])items.get(name); set(name, Containers.concat(v, values)); } catch (ClassCastException cce) { throw new ConfigurationException(name + " already exists with a different type of value"); } } /** * Adds the given string values to the item with the given name. * If an item with the given name does not exist, it is created. * * @param name the item's name * @param values the string values * @throws NullPointerException if name or values is null * @throws ConfigurationException if an item with the given name already exists, * but its value is of a different type */ protected void add(String name, String... values) throws ConfigurationException { if (name == null || values == null) throw new NullPointerException(); try { String[] v = (String[])items.get(name); set(name, Containers.concat(v, values)); } catch (ClassCastException cce) { throw new ConfigurationException(name + " already exists with a different type of value"); } } /** * Returns whether this configuration section contains an item with the given name. * * @param name the name to check * @return true if an item with the given name exists, false otherwise */ public boolean contains(String name) { return items.containsKey(name); } /** * Returns a copy of all items in this configuration section. * * @return a copy of all items in this configuration section */ public Map items() { return new LinkedHashMap<>(items); } /** * Returns a copy of all items whose values are configuration sections. * * @return a copy of all items whose values are configuration sections */ public Map sections() { Map m = new LinkedHashMap<>(items.size()); for (Map.Entry e : items.entrySet()) if (e.getValue() instanceof Configuration[]) m.put(e.getKey(), (Configuration[])e.getValue()); return m; } /** * Returns copy of all items whose values are strings. * * @return a copy of all items whose values are strings */ public Map allStrings() { Map m = new LinkedHashMap<>(items.size()); for (Map.Entry e : items.entrySet()) if (e.getValue() instanceof String[]) m.put(e.getKey(), (String[])e.getValue()); return m; } /** * Returns copy of all items whose values are strings, with only the first string value for each. * * @return a copy of all items whose values are strings */ public Map strings() { Map m = new LinkedHashMap<>(items.size()); for (Map.Entry e : items.entrySet()) if (e.getValue() instanceof String[]) m.put(e.getKey(), ((String[])e.getValue())[0]); return m; } /** * Returns a string representation of all configuration items. * * @return a string representation of all configuration items */ @Override public String toString() { StringBuilder sb = new StringBuilder(); for (Map.Entry e : items.entrySet()) sb.append(e.getKey()).append(": ").append(Strings.contentOfArray(e.getValue())).append('\n'); return sb.toString(); } /** * Returns the section values corresponding to the given name. * * @param name the name * @param required specifies the behavior if the given name is not found: * if true, an exception is thrown, and if false, an empty array * is returned * @return the section values corresponding to the given name, or an empty array * @throws ConfigurationException if the given name is not found and required is true */ public Configuration[] getSections(String name, boolean required) throws ConfigurationException { Configuration[] v; try { v = (Configuration[])items.get(name); } catch (ClassCastException cce) { throw new ConfigurationException(name + " values are of a different type"); } if (v == null) { if (required) throw new ConfigurationException("item not found: " + name); v = new Configuration[0]; } return v; } /** * Returns the section values corresponding to the given name. * * @param name the name * @return the section values corresponding to the given name * @throws ConfigurationException if the given name is not found */ public Configuration[] getSections(String name) throws ConfigurationException { return getSections(name, true); } /** * Returns the first section corresponding to the given name. * * @param name the name * @param required specifies the behavior if the given name is not found: * if true, an exception is thrown, and if false, null is returned * @return the first section corresponding to the given name, or null * @throws ConfigurationException if the given name is not found and required is true */ public Configuration getSection(String name, boolean required) throws ConfigurationException { Configuration[] v = getSections(name, required); return v.length > 0 ? v[0] : null; } /** * Returns the first section corresponding to the given name. * * @param name the name * @return the first section corresponding to the given name * @throws ConfigurationException if the given name is not found */ public Configuration getSection(String name) throws ConfigurationException { return getSections(name, true)[0]; } /** * Returns the string values corresponding to the given name. * * @param name the name * @param required specifies the behavior if the given name is not found: * if true, an exception is thrown, and if false, an empty array * is returned * @return the string values corresponding to the given name, or an empty array * @throws ConfigurationException if the given name is not found and required is true */ public String[] getStrings(String name, boolean required) throws ConfigurationException { String[] v; try { v = (String[])items.get(name); } catch (ClassCastException cce) { throw new ConfigurationException(name + " values are of a different type"); } if (v == null) { if (required) throw new ConfigurationException("item not found: " + name); v = new String[0]; } return v; } /** * Returns the string values corresponding to the given name. * * @param name the name * @return the string values corresponding to the given name * @throws ConfigurationException if the given name is not found */ public String[] getStrings(String name) throws ConfigurationException { return getStrings(name, true); } /** * Returns the string value corresponding to the given name. * * @param name the name * @return the string value corresponding to the given name * @throws ConfigurationException if there is no value corresponding to the given name */ public String get(String name) throws ConfigurationException { return getStrings(name, true)[0]; } /** * Returns the string value corresponding to the given name, or the given default value if none exists. * * @param name the name * @param def the default value * @return the string value corresponding to the given name, or the default value if none exists */ public String get(String name, String def) { String[] v = getStrings(name, false); return v.length > 0 ? v[0] : def; } /** * Returns the integer value corresponding to the given name. * * @param name the name * @return the integer value corresponding to the given name * @throws ConfigurationException if there is no value corresponding to the given name */ public int getInt(String name) throws ConfigurationException { return Integer.parseInt(get(name)); } /** * Returns the integer value corresponding to the given name, or the given default value if none exists. * * @param name the name * @param def the default value * @return the integer value corresponding to the given name, or the default value if none exists */ public int getInt(String name, int def) { String v = get(name, null); return v == null ? def : Integer.parseInt(v); } /** * Returns the long value corresponding to the given name. * * @param name the name * @return the long value corresponding to the given name * @throws ConfigurationException if there is no value corresponding to the given name */ public long getLong(String name) throws ConfigurationException { return Long.parseLong(get(name)); } /** * Returns the long value corresponding to the given name, or the given default value if none exists. * * @param name the name * @param def the default value * @return the long value corresponding to the given name, or the default value if none exists */ public long getLong(String name, long def) { String v = get(name, null); return v == null ? def : Long.parseLong(v); } /** * Returns the boolean value corresponding to the given name. * * @param name the name * @return the boolean value corresponding to the given name * @throws ConfigurationException if there is no value corresponding to the given name */ public boolean getBoolean(String name) throws ConfigurationException { return Boolean.parseBoolean(get(name)); } /** * Returns the boolean value corresponding to the given name, or the given default value if none exists. * * @param name the name * @param def the default value * @return the boolean value corresponding to the given name, or the default value if none exists */ public boolean getBoolean(String name, boolean def) { String v = get(name, null); return v == null ? def : Boolean.parseBoolean(v); } /** * Returns the enum value corresponding to the given name. The value is looked up in uppercase form. * * @param the enum type * @param name the name * @param enumType the Class object of the enum type from which to return a constant * @return the enum value corresponding to the given name * @throws ConfigurationException if there is no value corresponding to the given name * @throws IllegalArgumentException if the specified enum type has * no constant with the configuration value's name, or the specified * class object does not represent an enum type */ public > T getEnum(String name, Class enumType) throws ConfigurationException { return Enum.valueOf(enumType, get(name).toUpperCase()); } /** * Returns the enum value corresponding to the given name, or the given default value if none exists. * The value is looked up in uppercase form. * * @param the enum type * @param name the name * @param enumType the Class object of the enum type from which to return a constant * @param def the default value * @return the enum value corresponding to the given name, or the default value if none exists * @throws IllegalArgumentException if the specified enum type has * no constant with the configuration value's name, or the specified * class object does not represent an enum type */ public > T getEnum(String name, Class enumType, T def) { String v = get(name, null); return v == null ? def : Enum.valueOf(enumType, v.toUpperCase()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy