/** * 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.transform.scale.internal; import java.io.FileReader; import java.io.IOException; import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.smarthome.core.transform.AbstractFileTransformationService; import org.eclipse.smarthome.core.transform.TransformationException; import org.eclipse.smarthome.core.transform.TransformationService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The implementation of {@link TransformationService} which transforms the * input by matching it between limits of ranges in a scale file * * @author Gaƫl L'hopital * @author Markus Rathgeb - drop usage of Guava */ public class ScaleTransformationService extends AbstractFileTransformationService<Map<Range, String>> { private final Logger logger = LoggerFactory.getLogger(ScaleTransformationService.class); /** RegEx to extract a scale definition */ private static final Pattern LIMITS_PATTERN = Pattern.compile("(\\[|\\])(.*)\\.\\.(.*)(\\[|\\])"); /** * <p> * Transforms the input <code>source</code> by matching searching the range where it fits * i.e. [min..max]=value or ]min..max]=value * </p> * * @param properties * the list of properties defining all the available ranges * @param source * the input to transform * * @{inheritDoc * */ @Override protected String internalTransform(Map<Range, String> data, String source) throws TransformationException { try { final BigDecimal value = new BigDecimal(source); for (final Range range : data.keySet()) { if (range.contains(value)) { return data.get(range); } } throw new TransformationException("No matching range for '" + source + "'"); } catch (NumberFormatException e) { throw new TransformationException("Scale can only be used with numeric inputs"); } } @Override protected Map<Range, String> internalLoadTransform(String filename) throws TransformationException { try (FileReader reader = new FileReader(filename)) { final Properties properties = new Properties(); properties.load(reader); final Map<Range, String> data = new HashMap<>(); for (Entry<Object, Object> f : properties.entrySet()) { final String key = (String) f.getKey(); final String value = properties.getProperty(key); final Matcher matcher = LIMITS_PATTERN.matcher(key); if (matcher.matches() && (matcher.groupCount() == 4)) { final boolean lowerInclusive = matcher.group(1).equals("]") ? false : true; final boolean upperInclusive = matcher.group(4).equals("[") ? false : true; final String lowLimit = matcher.group(2); final String highLimit = matcher.group(3); final BigDecimal lowValue; final BigDecimal highValue; try { if (lowLimit.isEmpty()) { lowValue = null; } else { lowValue = new BigDecimal(lowLimit); } if (highLimit.isEmpty()) { highValue = null; } else { highValue = new BigDecimal(highLimit); } } catch (final NumberFormatException ex) { throw new TransformationException("Error parsing bounds: " + lowLimit + ".." + highLimit); } final Range range = Range.range(lowValue, lowerInclusive, highValue, upperInclusive); data.put(range, value); } else { logger.warn("Scale transform entry does not comply with syntax : '{}', '{}'", key, value); } } return data; } catch (final IOException ex) { throw new TransformationException("An error occurred while opening file.", ex); } } }