2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.modbus.internal;
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.Optional;
18 import java.util.regex.Matcher;
19 import java.util.regex.Pattern;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.core.library.types.DecimalType;
24 import org.openhab.core.library.types.OnOffType;
25 import org.openhab.core.library.types.OpenClosedType;
26 import org.openhab.core.transform.TransformationException;
27 import org.openhab.core.transform.TransformationHelper;
28 import org.openhab.core.transform.TransformationService;
29 import org.openhab.core.types.Command;
30 import org.openhab.core.types.TypeParser;
31 import org.osgi.framework.BundleContext;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
36 * Class describing transformation of a command or state.
38 * Inspired from other openHAB binding "Transformation" classes.
40 * @author Sami Salonen - Initial contribution
44 public class SingleValueTransformation implements ValueTransformation {
46 public static final String TRANSFORM_DEFAULT = "default";
47 public static final ValueTransformation IDENTITY_TRANSFORMATION = new SingleValueTransformation(TRANSFORM_DEFAULT,
50 /** RegEx to extract and parse a function String <code>'(.*?)\((.*)\)'</code> */
51 private static final Pattern EXTRACT_FUNCTION_PATTERN_OLD = Pattern.compile("(?<service>.*?)\\((?<arg>.*)\\)");
52 private static final Pattern EXTRACT_FUNCTION_PATTERN_NEW = Pattern.compile("(?<service>.*?):(?<arg>.*)");
55 * Ordered list of types that are tried out first when trying to parse transformed command
57 private static final List<Class<? extends Command>> DEFAULT_TYPES = new ArrayList<>();
59 DEFAULT_TYPES.add(DecimalType.class);
60 DEFAULT_TYPES.add(OpenClosedType.class);
61 DEFAULT_TYPES.add(OnOffType.class);
64 private final Logger logger = LoggerFactory.getLogger(SingleValueTransformation.class);
66 private final @Nullable String transformation;
67 final @Nullable String transformationServiceName;
68 final @Nullable String transformationServiceParam;
72 * @param transformation either FUN(VAL) (standard transformation syntax), default (identity transformation
73 * (output equals input)) or some other value (output is a constant). Futhermore, empty string is
74 * considered the same way as "default".
76 public SingleValueTransformation(@Nullable String transformation) {
77 this.transformation = transformation;
79 // Parse transformation configuration here on construction, but delay the
80 // construction of TransformationService to call-time
81 if (transformation == null || transformation.isEmpty() || transformation.equalsIgnoreCase(TRANSFORM_DEFAULT)) {
82 // no-op (identity) transformation
83 transformationServiceName = null;
84 transformationServiceParam = null;
86 int colonIndex = transformation.indexOf(":");
87 int parenthesisOpenIndex = transformation.indexOf("(");
89 final Matcher matcher;
90 if (parenthesisOpenIndex != -1 && (colonIndex == -1 || parenthesisOpenIndex < colonIndex)) {
91 matcher = EXTRACT_FUNCTION_PATTERN_OLD.matcher(transformation);
93 matcher = EXTRACT_FUNCTION_PATTERN_NEW.matcher(transformation);
95 if (matcher.matches()) {
98 transformationServiceName = matcher.group("service");
99 transformationServiceParam = matcher.group("arg");
102 "Given transformation configuration '{}' did not match the FUN(VAL) pattern. Transformation output will be constant '{}'",
103 transformation, transformation);
104 transformationServiceName = null;
105 transformationServiceParam = null;
111 * For testing, thus package visibility by design
113 * @param transformation
114 * @param transformationServiceName
115 * @param transformationServiceParam
117 SingleValueTransformation(String transformation, @Nullable String transformationServiceName,
118 @Nullable String transformationServiceParam) {
119 this.transformation = transformation;
120 this.transformationServiceName = transformationServiceName;
121 this.transformationServiceParam = transformationServiceParam;
125 public String transform(BundleContext context, String value) {
126 String transformedResponse;
127 String transformationServiceName = this.transformationServiceName;
128 String transformationServiceParam = this.transformationServiceParam;
130 if (transformationServiceName != null) {
132 if (transformationServiceParam == null) {
133 throw new TransformationException(
134 "transformation service parameter is missing! Invalid transform?");
137 TransformationService transformationService = TransformationHelper.getTransformationService(context,
138 transformationServiceName);
139 if (transformationService != null) {
140 transformedResponse = transformationService.transform(transformationServiceParam, value);
142 transformedResponse = value;
143 logger.warn("couldn't transform response because transformationService of type '{}' is unavailable",
144 transformationServiceName);
146 } catch (TransformationException te) {
147 logger.error("transformation throws exception [transformation={}, response={}]", transformation, value,
150 // in case of an error we return the response without any
152 transformedResponse = value;
154 } else if (isIdentityTransform()) {
155 // identity transformation
156 transformedResponse = value;
159 transformedResponse = this.transformation;
162 return transformedResponse == null ? "" : transformedResponse;
166 public boolean isIdentityTransform() {
167 return TRANSFORM_DEFAULT.equalsIgnoreCase(this.transformation);
170 public static Optional<Command> tryConvertToCommand(String transformed) {
171 return Optional.ofNullable(TypeParser.parseCommand(DEFAULT_TYPES, transformed));
175 public String toString() {
176 return "SingleValueTransformation [transformation=" + transformation + ", transformationServiceName="
177 + transformationServiceName + ", transformationServiceParam=" + transformationServiceParam + "]";