diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java index bbcdd1bf00..283044b279 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java @@ -16,6 +16,7 @@ package org.thingsboard.script.api.tbel; import com.google.common.primitives.Bytes; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; import org.mvel2.ExecutionContext; import org.mvel2.ParserConfiguration; @@ -29,32 +30,49 @@ import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; +import java.net.URLDecoder; +import java.net.URLEncoder; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoUnit; -import java.time.temporal.TemporalAccessor; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.regex.Matcher; +@Slf4j public class TbUtils { private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII); + private static final LinkedHashMap mdnEncodingReplacements = new LinkedHashMap<>(); + + static { + mdnEncodingReplacements.put("\\+", "%20"); + mdnEncodingReplacements.put("%21", "!"); + mdnEncodingReplacements.put("%27", "'"); + mdnEncodingReplacements.put("%28", "\\("); + mdnEncodingReplacements.put("%29", "\\)"); + mdnEncodingReplacements.put("%7E", "~"); + mdnEncodingReplacements.put("%3B", ";"); + mdnEncodingReplacements.put("%2C", ","); + mdnEncodingReplacements.put("%2F", "/"); + mdnEncodingReplacements.put("%3F", "\\?"); + mdnEncodingReplacements.put("%3A", ":"); + mdnEncodingReplacements.put("%40", "@"); + mdnEncodingReplacements.put("%26", "&"); + mdnEncodingReplacements.put("%3D", "="); + mdnEncodingReplacements.put("%2B", "\\+"); + mdnEncodingReplacements.put("%24", Matcher.quoteReplacement("$")); + mdnEncodingReplacements.put("%23", "#"); + } + public static void register(ParserConfiguration parserConfig) throws Exception { parserConfig.addImport("btoa", new MethodStub(TbUtils.class.getMethod("btoa", String.class))); @@ -175,6 +193,10 @@ public class TbUtils { ExecutionContext.class, Map.class, List.class))); parserConfig.addImport("toFlatMap", new MethodStub(TbUtils.class.getMethod("toFlatMap", ExecutionContext.class, Map.class, List.class, boolean.class))); + parserConfig.addImport("encodeURI", new MethodStub(TbUtils.class.getMethod("encodeURI", + String.class))); + parserConfig.addImport("decodeURI", new MethodStub(TbUtils.class.getMethod("decodeURI", + String.class))); } public static String btoa(String input) { @@ -589,22 +611,6 @@ public class TbUtils { return BigDecimal.valueOf(value).setScale(precision, RoundingMode.HALF_UP).floatValue(); } - private static boolean isHexadecimal(String value) { - return value != null && (value.contains("0x") || value.contains("0X")); - } - - private static String prepareNumberString(String value) { - if (value != null) { - value = value.trim(); - if (isHexadecimal(value)) { - value = value.replace("0x", ""); - value = value.replace("0X", ""); - } - value = value.replace(",", "."); - } - return value; - } - public static ExecutionHashMap toFlatMap(ExecutionContext ctx, Map json) { return toFlatMap(ctx, json, new ArrayList<>(), true); } @@ -623,6 +629,24 @@ public class TbUtils { return map; } + + public static String encodeURI(String uri) { + String encoded = URLEncoder.encode(uri, StandardCharsets.UTF_8); + for (var entry : mdnEncodingReplacements.entrySet()) { + encoded = encoded.replaceAll(entry.getKey(), entry.getValue()); + } + return encoded; + } + + public static String decodeURI(String uri) { + ArrayList allKeys = new ArrayList<>(mdnEncodingReplacements.keySet()); + Collections.reverse(allKeys); + for (String strKey : allKeys) { + uri = uri.replaceAll(mdnEncodingReplacements.get(strKey), strKey); + } + return URLDecoder.decode(uri, StandardCharsets.UTF_8); + } + private static void parseRecursive(Object json, Map map, List excludeList, String path, boolean pathInKey) { if (json instanceof Map.Entry) { Map.Entry entry = (Map.Entry) json; @@ -663,6 +687,22 @@ public class TbUtils { } } + private static boolean isHexadecimal(String value) { + return value != null && (value.contains("0x") || value.contains("0X")); + } + + private static String prepareNumberString(String value) { + if (value != null) { + value = value.trim(); + if (isHexadecimal(value)) { + value = value.replace("0x", ""); + value = value.replace("0X", ""); + } + value = value.replace(",", "."); + } + return value; + } + private static boolean isValidRadix(String value, int radix) { for (int i = 0; i < value.length(); i++) { if (i == 0 && value.charAt(i) == '-') { diff --git a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java index c140559a0f..8325e69f66 100644 --- a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java +++ b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java @@ -31,13 +31,11 @@ import java.io.IOException; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.Random; @Slf4j - public class TbUtilsTest { private ExecutionContext ctx; @@ -474,7 +472,16 @@ public class TbUtilsTest { } } + @Test + public void encodeDecodeUri_Test() { + String uriOriginal = "-_.!~*'();/?:@&=+$,#ht://example.ж д a/path with spaces/?param1=Київ 1¶m2=Україна2"; + String uriEncodeExpected = "-_.!~*'();/?:@&=+$,#ht://example.%D0%B6%20%D0%B4%20a/path%20with%20spaces/?param1=%D0%9A%D0%B8%D1%97%D0%B2%201¶m2=%D0%A3%D0%BA%D1%80%D0%B0%D1%97%D0%BD%D0%B02"; + String uriEncodeActual = TbUtils.encodeURI(uriOriginal); + Assert.assertEquals(uriEncodeExpected, uriEncodeActual); + String uriDecodeActual = TbUtils.decodeURI(uriEncodeActual); + Assert.assertEquals(uriOriginal, uriDecodeActual); + } private static List toList(byte[] data) { List result = new ArrayList<>(data.length); for (Byte b : data) {