/**
* Copyright (c) 2014-2017 by the respective copyright holders.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.smarthome.config.core;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* {@link ConfigDescriptionRegistry} provides access to {@link ConfigDescription}s.
* It tracks {@link ConfigDescriptionProvider} OSGi services to collect all {@link ConfigDescription}s.
*
* @see ConfigDescriptionProvider
*
* @author Dennis Nobel - Initial contribution, added locale support
* @author Michael Grammling - Initial contribution
* @author Chris Jackson - Added compatibility with multiple ConfigDescriptionProviders. Added Config OptionProvider.
* @author Thomas Höfer - Added unit
*/
public class ConfigDescriptionRegistry {
private final List<ConfigOptionProvider> configOptionProviders = new CopyOnWriteArrayList<>();
private final List<ConfigDescriptionProvider> configDescriptionProviders = new CopyOnWriteArrayList<>();
protected void addConfigOptionProvider(ConfigOptionProvider configOptionProvider) {
if (configOptionProvider != null) {
configOptionProviders.add(configOptionProvider);
}
}
protected void removeConfigOptionProvider(ConfigOptionProvider configOptionProvider) {
if (configOptionProvider != null) {
configOptionProviders.remove(configOptionProvider);
}
}
protected void addConfigDescriptionProvider(ConfigDescriptionProvider configDescriptionProvider) {
if (configDescriptionProvider != null) {
configDescriptionProviders.add(configDescriptionProvider);
}
}
protected void removeConfigDescriptionProvider(ConfigDescriptionProvider configDescriptionProvider) {
if (configDescriptionProvider != null) {
configDescriptionProviders.remove(configDescriptionProvider);
}
}
/**
* Returns all config descriptions.
* <p>
* If more than one {@link ConfigDescriptionProvider} is registered for a specific URI, then the returned
* {@link ConfigDescription} collection will contain the data from all providers.
* <p>
* No checking is performed to ensure that multiple providers don't provide the same configuration data. It is up to
* the binding to ensure that multiple sources (eg static XML and dynamic binding data) do not contain overlapping
* information.
*
* @param locale
* locale
* @return all config descriptions or an empty collection if no config
* description exists
*/
public Collection<ConfigDescription> getConfigDescriptions(Locale locale) {
Map<URI, ConfigDescription> configMap = new HashMap<URI, ConfigDescription>();
// Loop over all providers
for (ConfigDescriptionProvider configDescriptionProvider : this.configDescriptionProviders) {
// And for each provider, loop over all their config descriptions
for (ConfigDescription configDescription : configDescriptionProvider.getConfigDescriptions(locale)) {
// See if there already exists a configuration for this URI in the map
ConfigDescription configFromMap = configMap.get(configDescription.getURI());
if (configFromMap != null) {
// Yes - Merge the groups and parameters
List<ConfigDescriptionParameter> parameters = new ArrayList<ConfigDescriptionParameter>();
parameters.addAll(configFromMap.getParameters());
parameters.addAll(configDescription.getParameters());
List<ConfigDescriptionParameterGroup> parameterGroups = new ArrayList<ConfigDescriptionParameterGroup>();
parameterGroups.addAll(configFromMap.getParameterGroups());
parameterGroups.addAll(configDescription.getParameterGroups());
// And add the combined configuration to the map
configMap.put(configDescription.getURI(),
new ConfigDescription(configDescription.getURI(), parameters, parameterGroups));
} else {
// No - Just add the new configuration to the map
configMap.put(configDescription.getURI(), configDescription);
}
}
}
// Now convert the map into the collection
Collection<ConfigDescription> configDescriptions = new ArrayList<ConfigDescription>(configMap.size());
for (ConfigDescription configDescription : configMap.values()) {
configDescriptions.add(configDescription);
}
return Collections.unmodifiableCollection(configDescriptions);
}
/**
* Returns all config descriptions.
*
* @return all config descriptions or an empty collection if no config
* description exists
*/
public Collection<ConfigDescription> getConfigDescriptions() {
return getConfigDescriptions(null);
}
/**
* Returns a config description for a given URI.
* <p>
* If more than one {@link ConfigDescriptionProvider} is registered for the requested URI, then the returned
* {@link ConfigDescription} will contain the data from all providers.
* <p>
* No checking is performed to ensure that multiple providers don't provide the same configuration data. It is up to
* the binding to ensure that multiple sources (eg static XML and dynamic binding data) do not contain overlapping
* information.
*
* @param uri
* the URI to which the config description to be returned (must
* not be null)
* @param locale
* locale
* @return config description or null if no config description exists for
* the given name
*/
public ConfigDescription getConfigDescription(URI uri, Locale locale) {
List<ConfigDescriptionParameter> parameters = new ArrayList<ConfigDescriptionParameter>();
List<ConfigDescriptionParameterGroup> parameterGroups = new ArrayList<ConfigDescriptionParameterGroup>();
boolean found = false;
for (ConfigDescriptionProvider configDescriptionProvider : this.configDescriptionProviders) {
ConfigDescription config = configDescriptionProvider.getConfigDescription(uri, locale);
if (config != null) {
found = true;
// Simply merge the groups and parameters
parameters.addAll(config.getParameters());
parameterGroups.addAll(config.getParameterGroups());
}
}
if (found) {
List<ConfigDescriptionParameter> parametersWithOptions = new ArrayList<ConfigDescriptionParameter>(
parameters.size());
for (ConfigDescriptionParameter parameter : parameters) {
parametersWithOptions.add(getConfigOptions(uri, parameter, locale));
}
// Return the new configuration description
return new ConfigDescription(uri, parametersWithOptions, parameterGroups);
} else {
// Otherwise null
return null;
}
}
/**
* Returns a config description for a given URI.
*
* @param uri
* the URI to which the config description to be returned (must
* not be null)
* @return config description or null if no config description exists for
* the given name
*/
public ConfigDescription getConfigDescription(URI uri) {
return getConfigDescription(uri, null);
}
/**
* Updates the config parameter options for a given URI and parameter
* <p>
* If more than one {@link ConfigOptionProvider} is registered for the requested URI, then the returned
* {@link ConfigDescriptionParameter} will contain the data from all providers.
* <p>
* No checking is performed to ensure that multiple providers don't provide the same options. It is up to
* the binding to ensure that multiple sources (eg static XML and dynamic binding data) do not contain overlapping
* information.
*
* @param uri
* the URI to which the options to be returned (must not be null)
* @param parameter
* the parameter requiring options to be updated
* @param locale
* locale
* @return config description
*/
private ConfigDescriptionParameter getConfigOptions(URI uri, ConfigDescriptionParameter parameter, Locale locale) {
List<ParameterOption> options = new ArrayList<ParameterOption>();
// Add all the existing options that may be provided by the initial config description provider
options.addAll(parameter.getOptions());
boolean found = false;
for (ConfigOptionProvider configOptionProvider : this.configOptionProviders) {
Collection<ParameterOption> newOptions = configOptionProvider.getParameterOptions(uri, parameter.getName(),
locale);
if (newOptions != null) {
found = true;
// Simply merge the options
options.addAll(newOptions);
}
}
if (found) {
// Return the new parameter
return new ConfigDescriptionParameter(parameter.getName(), parameter.getType(), parameter.getMinimum(),
parameter.getMaximum(), parameter.getStepSize(), parameter.getPattern(), parameter.isRequired(),
parameter.isReadOnly(), parameter.isMultiple(), parameter.getContext(), parameter.getDefault(),
parameter.getLabel(), parameter.getDescription(), options, parameter.getFilterCriteria(),
parameter.getGroupName(), parameter.isAdvanced(), parameter.getLimitToOptions(),
parameter.getMultipleLimit(), parameter.getUnit(), parameter.getUnitLabel(),
parameter.isVerifyable());
} else {
// Otherwise return the original parameter
return parameter;
}
}
}