Browse Source

Merge pull request #8404 from imbeacon/feature/tbel-function-toFlatMap

Feature TBEL function toFlatMap
pull/8429/head
Andrew Shvayka 3 years ago
committed by GitHub
parent
commit
64908e8d66
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 74
      common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java
  2. 100
      common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java

74
common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java

@ -18,7 +18,9 @@ package org.thingsboard.script.api.tbel;
import org.mvel2.ExecutionContext;
import org.mvel2.ParserConfiguration;
import org.mvel2.execution.ExecutionArrayList;
import org.mvel2.execution.ExecutionHashMap;
import org.mvel2.util.MethodStub;
import org.thingsboard.server.common.data.StringUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@ -27,8 +29,12 @@ import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class TbUtils {
@ -89,6 +95,14 @@ public class TbUtils {
byte[].class)));
parserConfig.addImport("bytesToHex", new MethodStub(TbUtils.class.getMethod("bytesToHex",
ExecutionArrayList.class)));
parserConfig.addImport("toFlatMap", new MethodStub(TbUtils.class.getMethod("toFlatMap",
ExecutionContext.class, Map.class)));
parserConfig.addImport("toFlatMap", new MethodStub(TbUtils.class.getMethod("toFlatMap",
ExecutionContext.class, Map.class, boolean.class)));
parserConfig.addImport("toFlatMap", new MethodStub(TbUtils.class.getMethod("toFlatMap",
ExecutionContext.class, Map.class, List.class)));
parserConfig.addImport("toFlatMap", new MethodStub(TbUtils.class.getMethod("toFlatMap",
ExecutionContext.class, Map.class, List.class, boolean.class)));
}
public static String btoa(String input) {
@ -225,7 +239,7 @@ public class TbUtils {
}
ExecutionArrayList<Byte> data = new ExecutionArrayList<>(ctx);
for (int i = 0; i < len; i += 2) {
data.add((byte)((Character.digit(hex.charAt(i), 16) << 4)
data.add((byte) ((Character.digit(hex.charAt(i), 16) << 4)
+ Character.digit(hex.charAt(i + 1), 16)));
}
return data;
@ -316,4 +330,62 @@ public class TbUtils {
}
return value;
}
public static ExecutionHashMap<String, Object> toFlatMap(ExecutionContext ctx, Map<String, Object> json) {
return toFlatMap(ctx, json, new ArrayList<>(), true);
}
public static ExecutionHashMap<String, Object> toFlatMap(ExecutionContext ctx, Map<String, Object> json, boolean pathInKey) {
return toFlatMap(ctx, json, new ArrayList<>(), pathInKey);
}
public static ExecutionHashMap<String, Object> toFlatMap(ExecutionContext ctx, Map<String, Object> json, List<String> excludeList) {
return toFlatMap(ctx, json, excludeList, true);
}
public static ExecutionHashMap<String, Object> toFlatMap(ExecutionContext ctx, Map<String, Object> json, List<String> excludeList, boolean pathInKey) {
ExecutionHashMap<String, Object> map = new ExecutionHashMap<>(16, ctx);
parseRecursive(json, map, excludeList, "", pathInKey);
return map;
}
private static void parseRecursive(Object json, Map<String, Object> map, List<String> excludeList, String path, boolean pathInKey) {
if (json instanceof Map.Entry) {
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) json;
if (StringUtils.isNotBlank(path)) {
path += ".";
}
if (excludeList.contains(entry.getKey())) {
return;
}
path += entry.getKey();
json = entry.getValue();
}
if (json instanceof Set || json instanceof List) {
String arrayPath = path + ".";
Object[] collection = ((Collection<?>) json).toArray();
for (int index = 0; index < collection.length; index++) {
parseRecursive(collection[index], map, excludeList, arrayPath + index, pathInKey);
}
} else if (json instanceof Map) {
Map<?, ?> node = (Map<?, ?>) json;
for (Map.Entry<?, ?> entry : node.entrySet()) {
parseRecursive(entry, map, excludeList, path, pathInKey);
}
} else {
if (pathInKey) {
map.put(path, json);
} else {
String key = path.substring(path.lastIndexOf('.') + 1);
if (StringUtils.isNumeric(key)) {
int pos = path.length();
for (int i = 0; i < 2; i++) {
pos = path.lastIndexOf('.', pos - 1);
}
key = path.substring(pos + 1);
}
map.put(key, json);
}
}
}
}

100
common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java

@ -15,15 +15,50 @@
*/
package org.thingsboard.script.api.tbel;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mvel2.ExecutionContext;
import org.mvel2.ParserContext;
import org.mvel2.SandboxedParserConfiguration;
import org.mvel2.execution.ExecutionArrayList;
import org.mvel2.execution.ExecutionHashMap;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Random;
@Slf4j
public class TbUtilsTest {
private ExecutionContext ctx;
@Before
public void before() {
SandboxedParserConfiguration parserConfig = ParserContext.enableSandboxedMode();
parserConfig.addImport("JSON", TbJson.class);
parserConfig.registerDataType("Date", TbDate.class, date -> 8L);
parserConfig.registerDataType("Random", Random.class, date -> 8L);
parserConfig.registerDataType("Calendar", Calendar.class, date -> 8L);
try {
TbUtils.register(parserConfig);
} catch (Exception e) {
log.error("Cannot register functions", e);
}
ctx = new ExecutionContext(parserConfig);
Assert.assertNotNull(ctx);
}
@After
public void after() {
ctx.stop();
}
@Test
public void parseHexToInt() {
Assert.assertEquals(0xAB, TbUtils.parseHexToInt("AB"));
@ -87,6 +122,71 @@ public class TbUtilsTest {
Assert.assertEquals(expected, TbUtils.parseBytesToInt(data, 0, 3, false));
}
@Test
public void toFlatMap() {
ExecutionHashMap<String, Object> inputMap = new ExecutionHashMap<>(16, ctx);
inputMap.put("name", "Alice");
inputMap.put("age", 30);
inputMap.put("devices", new ExecutionArrayList<>(List.of(
new ExecutionHashMap<>(16, ctx) {{
put("id", "dev001");
put("type", "sensor");
}},
new ExecutionHashMap<>(16, ctx) {{
put("id", "dev002");
put("type", "actuator");
}}
), ctx));
inputMap.put("settings", new ExecutionHashMap<>(16, ctx) {{
put("notifications", true);
put("timezone", "UTC-5");
put("params", new ExecutionHashMap<>(16, ctx) {{
put("param1", "value1");
put("param2", "value2");
put("param3", new ExecutionHashMap<>(16, ctx) {{
put("subParam1", "value1");
put("subParam2", "value2");
}});
}});
}});
ExecutionArrayList<String> excludeList = new ExecutionArrayList<>(ctx);
excludeList.addAll(List.of("age", "id", "param1", "subParam2"));
ExecutionHashMap<String, Object> expectedMapWithPath = new ExecutionHashMap<>(16, ctx);
expectedMapWithPath.put("name", "Alice");
expectedMapWithPath.put("devices.0.type", "sensor");
expectedMapWithPath.put("devices.1.type", "actuator");
expectedMapWithPath.put("settings.notifications", true);
expectedMapWithPath.put("settings.timezone", "UTC-5");
expectedMapWithPath.put("settings.params.param2", "value2");
expectedMapWithPath.put("settings.params.param3.subParam1", "value1");
ExecutionHashMap<String, Object> actualMapWithPaths = TbUtils.toFlatMap(ctx, inputMap, excludeList, true);
Assert.assertEquals(expectedMapWithPath, actualMapWithPaths);
ExecutionHashMap<String, Object> expectedMapWithoutPaths = new ExecutionHashMap<>(16, ctx);
expectedMapWithoutPaths.put("timezone", "UTC-5");
expectedMapWithoutPaths.put("name", "Alice");
expectedMapWithoutPaths.put("id", "dev002");
expectedMapWithoutPaths.put("subParam2", "value2");
expectedMapWithoutPaths.put("type", "actuator");
expectedMapWithoutPaths.put("subParam1", "value1");
expectedMapWithoutPaths.put("param1", "value1");
expectedMapWithoutPaths.put("notifications", true);
expectedMapWithoutPaths.put("age", 30);
expectedMapWithoutPaths.put("param2", "value2");
ExecutionHashMap<String, Object> actualMapWithoutPaths = TbUtils.toFlatMap(ctx, inputMap, false);
Assert.assertEquals(expectedMapWithoutPaths, actualMapWithoutPaths);
}
private static String keyToValue(String key, String extraSymbol) {
return key + "Value" + (extraSymbol == null ? "" : extraSymbol);
}
private static List<Byte> toList(byte[] data) {
List<Byte> result = new ArrayList<>(data.length);
for (Byte b : data) {

Loading…
Cancel
Save