mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[map] Add a way to customize inline-map delimiters (#17327)
* [map] Add a way to customize inline-map delimiters Signed-off-by: Jimmy Tanagra <jcode@tanagra.id.au> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
e93e0a360c
commit
0224c3a457
@ -21,11 +21,20 @@ To organize the various transformations one might use subfolders.
|
||||
|
||||
## Inline Map
|
||||
|
||||
Instead of providing the file name from which to load, the mapping table can be specified inline by prefixing it with the `|` character.
|
||||
The "key=value" pairs are separated with a semicolon (`;`) or a newline character.
|
||||
Instead of providing the file name from which to load, the mapping table can be specified inline by prefixing it with the pipe character `|` .
|
||||
|
||||
The inline map entries are delimited with semicolons (`;`) by default.
|
||||
For example, the following map function translates open/closed to ON/OFF: `|open=ON; closed=OFF`
|
||||
|
||||
The delimiters can be changed by adding `?delimiter=` immediately after the pipe character `|`.
|
||||
Some examples of changing to different delimiters:
|
||||
|
||||
- `|?delimiter=,online=ON,offline=OFF`
|
||||
- `|?delimiter=|online=ON|offline=OFF`
|
||||
- `|?delimiter=##online=ON##offline=OFF`
|
||||
|
||||
To use `?delimiter` as an actual map key, do not place it at the beginning of the map.
|
||||
|
||||
## Example
|
||||
|
||||
transform/binary.map:
|
||||
@ -54,7 +63,7 @@ The functionality of this `TransformationService` can be used in a `Profile` on
|
||||
To do so, it can be configured in the `.items` file as follows:
|
||||
|
||||
```java
|
||||
String <itemName> { channel="<channelUID>"[profile="transform:MAP", function="<filename>", sourceFormat="<valueFormat>"]}
|
||||
String <itemName> { channel="<channelUID>" [profile="transform:MAP", function="<filename>", sourceFormat="<valueFormat>" ] }
|
||||
```
|
||||
|
||||
The mapping filename (within the `transform` folder) has to be set in the `function` parameter.
|
||||
@ -62,3 +71,9 @@ The parameter `sourceFormat` is optional and can be used to format the input val
|
||||
If omitted the default is `%s`, so the input value will be put into the transformation without any format changes.
|
||||
|
||||
Please note: This profile is a one-way transformation, i.e. only values from a device towards the item are changed, the other direction is left untouched.
|
||||
|
||||
To use an inline map in the profile:
|
||||
|
||||
```java
|
||||
String <itemName> { channel="<channelUID>" [ profile="transform:MAP", function="|open=ON;closed=OFF" ] }
|
||||
```
|
||||
|
@ -20,6 +20,8 @@ import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -60,7 +62,9 @@ public class MapTransformationService
|
||||
private static final String PROFILE_CONFIG_URI = "profile:transform:MAP";
|
||||
private static final String CONFIG_PARAM_FUNCTION = "function";
|
||||
private static final Set<String> SUPPORTED_CONFIGURATION_TYPES = Set.of("map");
|
||||
private static final Pattern INLINE_MAP_CONFIG_PATTERN = Pattern.compile("\\s*\\|(?<map>.+)", Pattern.DOTALL);
|
||||
private static final String INLINE_MAP_DEFAULT_DELIMITER = ";";
|
||||
private static final Pattern INLINE_MAP_CONFIG_PATTERN = Pattern
|
||||
.compile("\\s*\\|(?:\\?delimiter=(?<delimiter>\\W+?))?(?<map>.+)", Pattern.DOTALL);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MapTransformationService.class);
|
||||
private final TransformationRegistry transformationRegistry;
|
||||
@ -87,9 +91,9 @@ public class MapTransformationService
|
||||
properties = cachedInlineMap.computeIfAbsent(function, f -> {
|
||||
Properties props = new Properties();
|
||||
String map = matcher.group("map").trim();
|
||||
if (!map.contains("\n")) {
|
||||
map = map.replace(";", "\n");
|
||||
}
|
||||
String delimiter = Objects.requireNonNull(Optional.ofNullable(matcher.group("delimiter"))
|
||||
.map(String::trim).orElse(INLINE_MAP_DEFAULT_DELIMITER));
|
||||
map = map.replace(delimiter, "\n");
|
||||
try {
|
||||
props.load(new StringReader(map));
|
||||
logger.trace("Parsed inline map configuration '{}'", props);
|
||||
|
@ -7,7 +7,16 @@
|
||||
<config-description uri="profile:transform:MAP">
|
||||
<parameter name="function" type="text" required="true">
|
||||
<label>Filename</label>
|
||||
<description>Filename containing the mapping information.</description>
|
||||
<description><![CDATA[Filename containing the mapping information.
|
||||
<br /><br />
|
||||
Inline map is supported, e.g. "|online=ON;offline=OFF".
|
||||
<br /><br />
|
||||
The inline map entries are delimited with semicolons ("<code>;</code>") by default.
|
||||
<br />
|
||||
To use a different delimiter, for example a comma: "<code>|?delimiter=,;online=ON,offline=OFF</code>"
|
||||
<br />
|
||||
To use "<code>?delimiter</code>" as an actual map key, do not place it at the beginning of the map.
|
||||
]]></description>
|
||||
<limitToOptions>false</limitToOptions>
|
||||
</parameter>
|
||||
<parameter name="sourceFormat" type="text">
|
||||
|
@ -1,7 +1,12 @@
|
||||
# add-on
|
||||
|
||||
addon.map.name = MAP transformation
|
||||
addon.map.description = Transforms the input by mapping it to another string.
|
||||
|
||||
# bundle config
|
||||
|
||||
profile-type.transform.MAP.label = MAP
|
||||
profile.config.transform.MAP.function.label = Filename
|
||||
profile.config.transform.MAP.function.description = Filename containing the mapping information.
|
||||
profile.config.transform.MAP.function.description = Filename containing the mapping information.<br /><br />Inline map is supported, e.g. "|online=ON;offline=OFF".<br /><br />The inline map entries are delimited with semicolons ("<code>;</code>") by default. <br /> To use a different delimiter, for example a comma: "<code>|?delimiter=,;online=ON,offline=OFF</code>" <br /> To use "<code>?delimiter</code>" as an actual map key, do not place it at the beginning of the map.
|
||||
profile.config.transform.MAP.sourceFormat.label = State Formatter
|
||||
profile.config.transform.MAP.sourceFormat.description = How to format the state on the channel before transforming it, i.e. %s or %.1f °C (default is %s).
|
||||
|
@ -159,12 +159,6 @@ public class MapTransformationServiceTest extends JavaTest {
|
||||
assertEquals("value2", processor.transform(transformation, "key2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiLineInlineMapTest() throws TransformationException {
|
||||
String transformation = "|key1=semicolons_arent_separators;1 \n key2 = value;2";
|
||||
assertEquals("value;2", processor.transform(transformation, "key2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultInlineTest() throws TransformationException {
|
||||
String transformation = "|key1=value1;key2=value;=default";
|
||||
@ -176,4 +170,22 @@ public class MapTransformationServiceTest extends JavaTest {
|
||||
String transformation = "|key1=value1;key2=value;=_source_";
|
||||
assertEquals("nonexistent", processor.transform(transformation, "nonexistent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customSeparatorTest() throws TransformationException {
|
||||
String transformation = "|?delimiter=,key1=value1;with;semicolons,key2;too=value2,?delimiter=value3";
|
||||
assertEquals("value1;with;semicolons", processor.transform(transformation, "key1"));
|
||||
assertEquals("value2", processor.transform(transformation, "key2;too"));
|
||||
assertEquals("value3", processor.transform(transformation, "?delimiter"));
|
||||
|
||||
transformation = "|?delimiter=||key1=value1;with;semicolons||key2;too=value2||?delimiter=value3";
|
||||
assertEquals("value1;with;semicolons", processor.transform(transformation, "key1"));
|
||||
assertEquals("value2", processor.transform(transformation, "key2;too"));
|
||||
assertEquals("value3", processor.transform(transformation, "?delimiter"));
|
||||
|
||||
transformation = "|key1=value1;key2=value2;?delimiter=value3";
|
||||
assertEquals("value1", processor.transform(transformation, "key1"));
|
||||
assertEquals("value2", processor.transform(transformation, "key2"));
|
||||
assertEquals("value3", processor.transform(transformation, "?delimiter"));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user