mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-02-04 19:34:05 +01:00
[mqtt.homeassistant] Implement regex_findall and regex_findall_index filters (#17895)
Signed-off-by: Cody Cutrer <cody@cutrer.us> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
63a8c29a0d
commit
7c8fe077f2
@ -53,11 +53,11 @@ public class HomeAssistantChannelTransformation extends ChannelTransformation {
|
|||||||
private final Logger logger = LoggerFactory.getLogger(HomeAssistantChannelTransformation.class);
|
private final Logger logger = LoggerFactory.getLogger(HomeAssistantChannelTransformation.class);
|
||||||
|
|
||||||
private final Jinjava jinjava;
|
private final Jinjava jinjava;
|
||||||
private final AbstractComponent component;
|
private final AbstractComponent<?> component;
|
||||||
private final String template;
|
private final String template;
|
||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
public HomeAssistantChannelTransformation(Jinjava jinjava, AbstractComponent component, String template) {
|
public HomeAssistantChannelTransformation(Jinjava jinjava, AbstractComponent<?> component, String template) {
|
||||||
super((String) null);
|
super((String) null);
|
||||||
this.jinjava = jinjava;
|
this.jinjava = jinjava;
|
||||||
this.component = component;
|
this.component = component;
|
||||||
|
@ -14,15 +14,24 @@ package org.openhab.binding.mqtt.homeassistant.internal;
|
|||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.re2j.Matcher;
|
||||||
|
import com.google.re2j.Pattern;
|
||||||
|
import com.google.re2j.PatternSyntaxException;
|
||||||
import com.hubspot.jinjava.interpret.Context;
|
import com.hubspot.jinjava.interpret.Context;
|
||||||
import com.hubspot.jinjava.interpret.InterpretException;
|
import com.hubspot.jinjava.interpret.InterpretException;
|
||||||
|
import com.hubspot.jinjava.interpret.InvalidArgumentException;
|
||||||
|
import com.hubspot.jinjava.interpret.InvalidReason;
|
||||||
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
|
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
|
||||||
|
import com.hubspot.jinjava.interpret.TemplateSyntaxException;
|
||||||
import com.hubspot.jinjava.lib.filter.Filter;
|
import com.hubspot.jinjava.lib.filter.Filter;
|
||||||
import com.hubspot.jinjava.lib.fn.ELFunctionDefinition;
|
import com.hubspot.jinjava.lib.fn.ELFunctionDefinition;
|
||||||
import com.hubspot.jinjava.util.ObjectTruthValue;
|
import com.hubspot.jinjava.util.ObjectTruthValue;
|
||||||
@ -39,15 +48,17 @@ public class HomeAssistantJinjaFunctionLibrary {
|
|||||||
new ELFunctionDefinition("", "iif", Functions.class, "iif", Object.class, Object[].class));
|
new ELFunctionDefinition("", "iif", Functions.class, "iif", Object.class, Object[].class));
|
||||||
context.registerFilter(new SimpleFilter("iif", Functions.class, "iif", Object.class, Object[].class));
|
context.registerFilter(new SimpleFilter("iif", Functions.class, "iif", Object.class, Object[].class));
|
||||||
context.registerFilter(new IsDefinedFilter());
|
context.registerFilter(new IsDefinedFilter());
|
||||||
|
context.registerFilter(new RegexFindAllFilter());
|
||||||
|
context.registerFilter(new RegexFindAllIndexFilter());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNullByDefault({})
|
@NonNullByDefault({})
|
||||||
private static class SimpleFilter implements Filter {
|
private static class SimpleFilter implements Filter {
|
||||||
private final String name;
|
private final String name;
|
||||||
private final Method method;
|
private final Method method;
|
||||||
private final Class klass;
|
private final Class<?> klass;
|
||||||
|
|
||||||
public SimpleFilter(String name, Class klass, String methodName, Class... args) {
|
public SimpleFilter(String name, Class<?> klass, String methodName, Class<?>... args) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.klass = klass;
|
this.klass = klass;
|
||||||
try {
|
try {
|
||||||
@ -110,9 +121,135 @@ public class HomeAssistantJinjaFunctionLibrary {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.home-assistant.io/docs/configuration/templating/#regular-expressions
|
||||||
|
// https://github.com/home-assistant/core/blob/2024.12.2/homeassistant/helpers/template.py#L2453
|
||||||
|
@NonNullByDefault({})
|
||||||
|
private static class RegexFindAllFilter implements Filter {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "regex_findall";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object filter(Object var, JinjavaInterpreter interpreter, String... args) {
|
||||||
|
if (args.length > 2) {
|
||||||
|
throw new TemplateSyntaxException(interpreter, getName(),
|
||||||
|
"requires at most 2 arguments (regex string, ignore case)");
|
||||||
|
}
|
||||||
|
|
||||||
|
String find = null;
|
||||||
|
if (args.length >= 1) {
|
||||||
|
find = args[0];
|
||||||
|
}
|
||||||
|
String ignoreCase = null;
|
||||||
|
if (args.length == 2) {
|
||||||
|
ignoreCase = args[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher m = regexFindAll(var, interpreter, find, ignoreCase);
|
||||||
|
|
||||||
|
List<Object> result = new ArrayList<>();
|
||||||
|
while (m.find()) {
|
||||||
|
result.add(resultForMatcher(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object resultForMatcher(Matcher m) {
|
||||||
|
if (m.groupCount() == 0) {
|
||||||
|
return m.group();
|
||||||
|
} else if (m.groupCount() == 1) {
|
||||||
|
return m.group(1);
|
||||||
|
} else {
|
||||||
|
List<String> groups = new ArrayList<>(m.groupCount());
|
||||||
|
for (int i = 1; i <= m.groupCount(); ++i) {
|
||||||
|
groups.add(m.group(i));
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Matcher regexFindAll(Object var, JinjavaInterpreter interpreter, String find, String ignoreCaseStr) {
|
||||||
|
String s;
|
||||||
|
if (var == null) {
|
||||||
|
s = "None";
|
||||||
|
} else {
|
||||||
|
s = var.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean ignoreCase = ObjectTruthValue.evaluate(ignoreCaseStr);
|
||||||
|
int flags = 0;
|
||||||
|
if (ignoreCase) {
|
||||||
|
flags = Pattern.CASE_INSENSITIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern p;
|
||||||
|
try {
|
||||||
|
if (find instanceof String findString) {
|
||||||
|
p = Pattern.compile(findString, flags);
|
||||||
|
} else if (find == null) {
|
||||||
|
p = Pattern.compile("", flags);
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgumentException(interpreter, this, InvalidReason.REGEX, 0, find);
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.matcher(s);
|
||||||
|
} catch (PatternSyntaxException e) {
|
||||||
|
throw new InvalidArgumentException(interpreter, this, InvalidReason.REGEX, 0, find);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.home-assistant.io/docs/configuration/templating/#regular-expressions
|
||||||
|
// https://github.com/home-assistant/core/blob/2024.12.2/homeassistant/helpers/template.py#L2448
|
||||||
|
@NonNullByDefault({})
|
||||||
|
private static class RegexFindAllIndexFilter extends RegexFindAllFilter {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "regex_findall_index";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object filter(Object var, JinjavaInterpreter interpreter, String... args) {
|
||||||
|
if (args.length > 3) {
|
||||||
|
throw new TemplateSyntaxException(interpreter, getName(),
|
||||||
|
"requires at most 3 arguments (regex string, index, ignore case)");
|
||||||
|
}
|
||||||
|
|
||||||
|
String find = null;
|
||||||
|
if (args.length >= 1) {
|
||||||
|
find = args[0];
|
||||||
|
}
|
||||||
|
int index = 0;
|
||||||
|
if (args.length >= 2) {
|
||||||
|
index = Integer.valueOf(args[1]);
|
||||||
|
if (index < 0) {
|
||||||
|
throw new InvalidArgumentException(interpreter, this, InvalidReason.POSITIVE_NUMBER, 1, args[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String ignoreCase = null;
|
||||||
|
if (args.length == 3) {
|
||||||
|
ignoreCase = args[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher m = regexFindAll(var, interpreter, find, ignoreCase);
|
||||||
|
int i = 0;
|
||||||
|
while (i <= index) {
|
||||||
|
if (!m.find()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultForMatcher(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class Functions {
|
private static class Functions {
|
||||||
// https://www.home-assistant.io/docs/configuration/templating/#immediate-if-iif
|
// https://www.home-assistant.io/docs/configuration/templating/#immediate-if-iif
|
||||||
public static Object iif(Object value, Object... results) {
|
public static @Nullable Object iif(@Nullable Object value, @Nullable Object... results) {
|
||||||
if (results.length > 3) {
|
if (results.length > 3) {
|
||||||
throw new IllegalArgumentException("Parameters for function 'iff' do not match");
|
throw new IllegalArgumentException("Parameters for function 'iff' do not match");
|
||||||
}
|
}
|
||||||
|
@ -100,6 +100,24 @@ public class HomeAssistantChannelTransformationTests {
|
|||||||
assertThat(transform("{{ 'hi' | is_defined }}", "{}"), is("hi"));
|
assertThat(transform("{{ 'hi' | is_defined }}", "{}"), is("hi"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRegexFindall() {
|
||||||
|
assertThat(transform("{{ 'Flight from JFK to LHR' | regex_findall('([A-Z]{3})') }}", ""), is("[JFK, LHR]"));
|
||||||
|
assertThat(transform(
|
||||||
|
"{{ 'button_up_press' | regex_findall('^(?P<button>(?:button_)?[a-z0-9]+)_(?P<action>(?:press|hold)(?:_release)?)$') }}",
|
||||||
|
""), is("[[button_up, press]]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRegexFindallIndex() {
|
||||||
|
assertThat(transform("{{ 'Flight from JFK to LHR' | regex_findall_index('([A-Z]{3})', 0) }}", ""), is("JFK"));
|
||||||
|
assertThat(transform("{{ 'Flight from JFK to LHR' | regex_findall_index('([A-Z]{3})', 1) }}", ""), is("LHR"));
|
||||||
|
assertThat(transform("{{ ['JFK', 'LHR'] | regex_findall_index('([A-Z]{3})', 1) }}", ""), is("LHR"));
|
||||||
|
assertThat(transform(
|
||||||
|
"{{ 'button_up_press' | regex_findall_index('^(?P<button>(?:button_)?[a-z0-9]+)_(?P<action>(?:press|hold)(?:_release)?)$') }}",
|
||||||
|
""), is("[button_up, press]"));
|
||||||
|
}
|
||||||
|
|
||||||
protected @Nullable String transform(String template, String value) {
|
protected @Nullable String transform(String template, String value) {
|
||||||
return transformation.apply(template, value).orElse(null);
|
return transformation.apply(template, value).orElse(null);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user