[transform.map] Refactor service to use TransformationConfigurationRegistry (#12433)
This is the reference implementation for using the new `TransformationConfigurationRegistry`. The localization tests can be omitted as this is covered by tests in the core component. Signed-off-by: Jan N. Klug <github@klug.nrw>
This commit is contained in:
@@ -12,22 +12,30 @@
|
||||
*/
|
||||
package org.openhab.transform.map.internal;
|
||||
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.common.registry.RegistryChangeListener;
|
||||
import org.openhab.core.config.core.ConfigOptionProvider;
|
||||
import org.openhab.core.config.core.ParameterOption;
|
||||
import org.openhab.core.transform.AbstractFileTransformationService;
|
||||
import org.openhab.core.transform.TransformationConfiguration;
|
||||
import org.openhab.core.transform.TransformationConfigurationRegistry;
|
||||
import org.openhab.core.transform.TransformationException;
|
||||
import org.openhab.core.transform.TransformationService;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -37,65 +45,100 @@ import org.slf4j.LoggerFactory;
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution and API
|
||||
* @author Gaël L'hopital - Make it localizable
|
||||
* @author Jan N. Klug - Refactored to use {@link TransformationConfigurationRegistry}
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = { TransformationService.class, ConfigOptionProvider.class }, property = {
|
||||
"openhab.transform=MAP" })
|
||||
public class MapTransformationService extends AbstractFileTransformationService<Properties>
|
||||
implements ConfigOptionProvider {
|
||||
|
||||
public class MapTransformationService
|
||||
implements TransformationService, ConfigOptionProvider, RegistryChangeListener<TransformationConfiguration> {
|
||||
private final Logger logger = LoggerFactory.getLogger(MapTransformationService.class);
|
||||
|
||||
private static final String PROFILE_CONFIG_URI = "profile:transform:MAP";
|
||||
private static final String CONFIG_PARAM_FUNCTION = "function";
|
||||
private static final String[] FILE_NAME_EXTENSIONS = { "map" };
|
||||
private static final Set<String> SUPPORTED_CONFIGURATION_TYPES = Set.of("map");
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Transforms the input <code>source</code> by mapping it to another string. It expects the mappings to be read from
|
||||
* a file which is stored under the 'configurations/transform' folder. This file should be in property syntax, i.e.
|
||||
* simple lines with "key=value" pairs. To organize the various transformations one might use subfolders.
|
||||
*
|
||||
* @param properties the list of properties which contains the key value pairs for the mapping.
|
||||
* @param source the input to transform
|
||||
* @return the transformed result or null if the transformation couldn't be completed for any reason.
|
||||
*/
|
||||
@Override
|
||||
protected @Nullable String internalTransform(Properties properties, String source) throws TransformationException {
|
||||
String target = properties.getProperty(source);
|
||||
private final TransformationConfigurationRegistry transformationConfigurationRegistry;
|
||||
private final Map<String, Properties> cachedTransformations = new ConcurrentHashMap<>();
|
||||
|
||||
if (target == null) {
|
||||
target = properties.getProperty("");
|
||||
if (target == null) {
|
||||
throw new TransformationException("Target value not found in map for '" + source + "'");
|
||||
}
|
||||
}
|
||||
@Activate
|
||||
public MapTransformationService(
|
||||
@Reference TransformationConfigurationRegistry transformationConfigurationRegistry) {
|
||||
this.transformationConfigurationRegistry = transformationConfigurationRegistry;
|
||||
transformationConfigurationRegistry.addRegistryChangeListener(this);
|
||||
}
|
||||
|
||||
logger.debug("Transformation resulted in '{}'", target);
|
||||
return target;
|
||||
@Deactivate
|
||||
public void deactivate() {
|
||||
transformationConfigurationRegistry.removeRegistryChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Properties internalLoadTransform(String filename) throws TransformationException {
|
||||
Properties result = new Properties();
|
||||
try (FileReader reader = new FileReader(filename)) {
|
||||
result.load(reader);
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new TransformationException("An error occurred while opening file.", e);
|
||||
public @Nullable String transform(String function, String source) throws TransformationException {
|
||||
// always get a configuration from the registry to account for changed system locale
|
||||
TransformationConfiguration transformationConfiguration = transformationConfigurationRegistry.get(function,
|
||||
null);
|
||||
|
||||
if (transformationConfiguration != null) {
|
||||
if (!cachedTransformations.containsKey(transformationConfiguration.getUID())) {
|
||||
importConfiguration(transformationConfiguration);
|
||||
}
|
||||
Properties properties = cachedTransformations.get(function);
|
||||
if (properties != null) {
|
||||
String target = properties.getProperty(source);
|
||||
|
||||
if (target == null) {
|
||||
target = properties.getProperty("");
|
||||
if (target == null) {
|
||||
throw new TransformationException("Target value not found in map for '" + source + "'");
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Transformation resulted in '{}'", target);
|
||||
return target;
|
||||
}
|
||||
}
|
||||
throw new TransformationException("Could not find configuration '" + function + "' or failed to parse it.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Collection<ParameterOption> getParameterOptions(URI uri, String param, @Nullable String context,
|
||||
@Nullable Locale locale) {
|
||||
if (PROFILE_CONFIG_URI.equals(uri.toString())) {
|
||||
switch (param) {
|
||||
case CONFIG_PARAM_FUNCTION:
|
||||
return getFilenames(FILE_NAME_EXTENSIONS).stream().map(f -> new ParameterOption(f, f))
|
||||
.collect(Collectors.toList());
|
||||
if (CONFIG_PARAM_FUNCTION.equals(param)) {
|
||||
return transformationConfigurationRegistry.getConfigurations(SUPPORTED_CONFIGURATION_TYPES).stream()
|
||||
.map(c -> new ParameterOption(c.getUID(), c.getLabel())).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void added(TransformationConfiguration element) {
|
||||
// do nothing, configurations are added to cache if needed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(TransformationConfiguration element) {
|
||||
cachedTransformations.remove(element.getUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updated(TransformationConfiguration oldElement, TransformationConfiguration element) {
|
||||
if (cachedTransformations.remove(oldElement.getUID()) != null) {
|
||||
// import only if it was present before
|
||||
importConfiguration(element);
|
||||
}
|
||||
}
|
||||
|
||||
private void importConfiguration(@Nullable TransformationConfiguration configuration) {
|
||||
if (configuration != null) {
|
||||
try {
|
||||
Properties properties = new Properties();
|
||||
properties.load(new StringReader(configuration.getContent()));
|
||||
cachedTransformations.put(configuration.getUID(), properties);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user