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

net.obvj.confectory.ConfigurationContainer Maven / Gradle / Ivy

Go to download

Core part of Confectory that defines the base API and provides basic functionality using standard Java features

There is a newer version: 2.5.0
Show newest version
/*
 * Copyright 2021 obvj.net
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.obvj.confectory;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import net.obvj.confectory.settings.ConfectorySettings;

/**
 * An object that holds multiple {@code Configuration} objects and retrieves configuration
 * data seamlessly, by namespace and key.
 * 

* The {@code Configuration} objects are sorted by precedence (from highest to lowest). * So, in case of key collision, the object with the highest precedence will be selected * first. The container may still select other lower-precedence {@code Configuration} * objects if a key is not found in the highest-precedence {@code Configuration}. *

* To create a new empty container, use the default constructor * {@code new ConfigurationContainer()}. To create a new container with preset * {@code Configuration} objects, pass them as "var-args". *

* Use the {@code add(Configuration)} method at any time to register new objects inside * the container. *

* To retrieve {@code Configuration} data, use any of the getter methods, specifying a * namespace and key. *

* Single-argument getter methods can be used to retrieve data from {@code Configuration} * objects that do not have a declared namespace. These methods may also retrieve data * from all {@code Configuration} objects if the selected data-fetch strategy is * {@code LENIENT}. *

* Each container may have a custom {@link DataFetchStrategy} which may be specified at * container construction time and modified using the setter method anytime. If not * specified, the container uses the default choice configured via * {@link ConfectorySettings}. *

* IMPORTANT: This class works only with map-based {@code Configuration} * objects (e.g.: {@code Properties}, {@code JSONObject}, {@code JsonNode}, etc.). In * other words, only "container" objects which values can be accessed using either a key * or path expression (e.g. {@code JSONPath}). POJO-based {@code Configuration} objects * are not supported inside a the {@link ConfigurationContainer}. * * @author oswaldo.bapvic.jr (Oswaldo Junior) * @since 0.1.0 * * @see Configuration * @see DataFetchStrategy */ public class ConfigurationContainer { protected static final String DEFAULT_NAMESPACE = ""; private Map>> configMap = new HashMap<>(); private DataFetchStrategy dataFetchStrategy; /** * Builds a new {@code ConfigurationContainer} with an arbitrary number of preset * {@code Configuration} objects to be registered. * * @param configs an arbitrary number of {@code Configuration} objects (zero or more) to * be registered at constructor time */ public ConfigurationContainer(Configuration... configs) { this(null, configs); } /** * Builds a new {@code ConfigurationContainer} with a custom {@link DataFetchStrategy} and * an arbitrary number of preset {@code Configuration} objects. * * @param dataFetchStrategy an optional {@link DataFetchStrategy} to be applied by this * container; {@code null} is allowed and means the default * strategy will be applied * @param configs an arbitrary number of {@code Configuration} objects (zero or * more) to be registered at constructor time */ public ConfigurationContainer(DataFetchStrategy dataFetchStrategy, Configuration... configs) { ConfectorySettings settings = Confectory.settings(); setDataFetchStrategy(ObjectUtils.defaultIfNull(dataFetchStrategy, settings.getDefaultDataFetchStrategy())); Arrays.stream(configs).forEach(this::add); } /** * Returns the {@code DataFetchStrategy} associated with this container. * * @return a {@link DataFetchStrategy} */ public DataFetchStrategy getDataFetchStrategy() { return dataFetchStrategy; } /** * Defines a custom {@code DataFetchStrategy} for this container. * * @param strategy the {@link DataFetchStrategy} to set; not null * @throws NullPointerException if the specified {@code strategy} is null */ public void setDataFetchStrategy(DataFetchStrategy strategy) { dataFetchStrategy = Objects.requireNonNull(strategy, "the DataFetchStrategy must not be null"); } /** * Adds the specified {@code Configuration} to this container. * * @param configuration the {@link Configuration} to be added to the container */ public void add(Configuration configuration) { String namespace = parseNamespace(configuration.getNamespace()); Set> configSet = configMap.computeIfAbsent(namespace, k -> new HashSet<>()); configSet.add(configuration); } /** * Copies all of the {@code Configuration} objects from another container to this * container. * * @param source the source container which {@code Configuration} objects are to be stored * in this container; {@code null} is allowed */ public void addAll(ConfigurationContainer source) { if (source != null) { source.configMap.values().stream().flatMap(Collection::stream).forEach(this::add); } } /** * Removes all of the {@code Configuration} objects from this container. */ public void clear() { configMap.clear(); } /** * Returns the {@code Boolean} object associated with the specified {@code key} in the * default namespace (or in all namespaces depending on the {@link DataFetchStrategy} in * scope). * * @param key the object key (or path) * @return the {@code Boolean} object associated with the specified {@code key}; * {@code null} if not found * * @see DataFetchStrategy */ public Boolean getBoolean(String key) { return getBoolean(DEFAULT_NAMESPACE, key); } /** * Returns the {@code Boolean} object associated with the specified {@code key} in the * specified {@code namespace}. * * @param namespace the namespace to be used * @param key the object key (or path) * @return the {@code Boolean} object associated with the specified {@code key}; * {@code null} if not found */ public Boolean getBoolean(String namespace, String key) { return getValue(namespace, config -> config.getBoolean(key)); } /** * Returns the {@code Integer} object associated with the specified {@code key} in the * default namespace (or in all namespaces depending on the {@link DataFetchStrategy} in * scope). * * @param key the object key (or path) * @return the {@code Integer} object associated with the specified {@code key}; * {@code null} if not found * * @see DataFetchStrategy */ public Integer getInteger(String key) { return getInteger(DEFAULT_NAMESPACE, key); } /** * Returns the {@code Integer} object associated with the specified {@code key} in the * specified {@code namespace}. * * @param namespace the namespace to be used * @param key the object key (or path) * @return the {@code Integer} object associated with the specified {@code key}; * {@code null} if not found */ public Integer getInteger(String namespace, String key) { return getValue(namespace, config -> config.getInteger(key)); } /** * Returns the {@code Long} object associated with the specified {@code key} in the * default namespace (or in all namespaces depending on the {@link DataFetchStrategy} in * scope). * * @param key the object key (or path) * @return the {@code Long} object associated with the specified {@code key}; {@code null} * if not found * * @see DataFetchStrategy */ public Long getLong(String key) { return getLong(DEFAULT_NAMESPACE, key); } /** * Returns the {@code Long} object associated with the specified {@code key} in the * specified {@code namespace}. * * @param namespace the namespace to be used * @param key the object key (or path) * @return the {@code Long} object associated with the specified {@code key}; {@code null} * if not found */ public Long getLong(String namespace, String key) { return getValue(namespace, config -> config.getLong(key)); } /** * Returns the {@code Double} object associated with the specified {@code key} in the * default namespace (or in all namespaces depending on the {@link DataFetchStrategy} in * scope). * * @param key the object key (or path) * @return the {@code Double} value associated with the specified {@code key}; * {@code null} if not found * * @see DataFetchStrategy */ public Double getDouble(String key) { return getDouble(DEFAULT_NAMESPACE, key); } /** * Returns the {@code Double} object associated with the specified {@code key} in the * specified {@code namespace}. * * @param namespace the namespace to be used * @param key the object key (or path) * @return the {@code Double} object associated with the specified {@code key}; * {@code null} if not found */ public Double getDouble(String namespace, String key) { return getValue(namespace, config -> config.getDouble(key)); } /** * Returns the {@code String} object associated with the specified {@code key} in the * default namespace (or in all namespaces depending on the {@link DataFetchStrategy} in * scope). * * @param key the object key (or path) * @return the {@code String} object associated with the specified {@code key}; * {@code null} if not found * * @see DataFetchStrategy */ public String getString(String key) { return getString(DEFAULT_NAMESPACE, key); } /** * Returns the {@code String} object associated with the specified {@code key} in the * specified {@code namespace}. * * @param namespace the namespace to be used * @param key the object key (or path) * @return the {@code String} object associated with the specified {@code key}; * {@code null} if not found */ public String getString(String namespace, String key) { return getValue(namespace, config -> config.getString(key)); } /** * Template method for retrieving properties from the {@code configMap}. * * @param the value return type * @param namespace the namespace which property is to be fetched * @param mainFunction the main data fetch function; applies a particular method to the * {@code Configuration} objects in process * * @return the value evaluated by the {@code mainFunction}, or {@code null} if not found */ protected T getValue(String namespace, Function, T> mainFunction) { Iterator> iterator = getConfigurationStream(namespace).iterator(); while (iterator.hasNext()) { Configuration config = iterator.next(); T value = mainFunction.apply(config); if (value != null) { return value; } } return null; } /** * Retrieves a stream of {@link Configuration} objects on a given namespace. * * @param namespace the namespace to be searched * @return a stream of {@link Configuration} objects or an empty stream, never * {@code null} */ private Stream> getConfigurationStream(String namespace) { return dataFetchStrategy.getConfigurationStream(namespace, configMap); } /** * Returns either the passed namespace, or the value defined for * {@code DEFAULT_NAMESPACE}, if the passed argument is empty or null * * @param namespace the namespace to the checked * @return the passed namespace, or the value of {@code DEFAULT_NAMESPACE}, never * {@code null} */ private String parseNamespace(String namespace) { return StringUtils.defaultString(namespace, DEFAULT_NAMESPACE); } /** * Returns the number of {@code Configuration} objects in this container. * * @return the number of {@code Configuration} objects in this container * @since 1.0.0 */ public long size() { return configMap.values().stream().mapToInt(Set::size).sum(); } /** * Returns the number of {@code Configuration} objects associated with the specified * {@code namespace} in this container. * * @param namespace the namespace to be tested * @return the number of {@code Configuration} objects associated with the specified * {@code namespace} */ public long size(String namespace) { return configMap.getOrDefault(parseNamespace(namespace), Collections.emptySet()).size(); } /** * Returns {@code true} if this container contains no {@code Configuration} objects. * * @return {@code true} if this container contains no {@code Configuration} objects * @since 1.0.0 */ public boolean isEmpty() { return configMap.isEmpty(); } /** * Returns all of the namespaces defined inside this container. * * @return a set of namespaces */ public Collection getNamespaces() { return configMap.keySet(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy