@ -15,6 +15,8 @@
* /
package org.thingsboard.script.api.tbel ;
import com.google.common.primitives.Bytes ;
import org.apache.commons.lang3.ArrayUtils ;
import org.mvel2.ExecutionContext ;
import org.mvel2.ParserConfiguration ;
import org.mvel2.execution.ExecutionArrayList ;
@ -25,11 +27,13 @@ import org.thingsboard.server.common.data.StringUtils;
import java.io.IOException ;
import java.io.UnsupportedEncodingException ;
import java.math.BigDecimal ;
import java.math.BigInteger ;
import java.math.RoundingMode ;
import java.nio.ByteBuffer ;
import java.nio.ByteOrder ;
import java.nio.charset.StandardCharsets ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Base64 ;
import java.util.Collection ;
import java.util.List ;
@ -61,6 +65,10 @@ public class TbUtils {
String . class ) ) ) ;
parserConfig . addImport ( "parseInt" , new MethodStub ( TbUtils . class . getMethod ( "parseInt" ,
String . class , int . class ) ) ) ;
parserConfig . addImport ( "parseLong" , new MethodStub ( TbUtils . class . getMethod ( "parseLong" ,
String . class ) ) ) ;
parserConfig . addImport ( "parseLong" , new MethodStub ( TbUtils . class . getMethod ( "parseLong" ,
String . class , int . class ) ) ) ;
parserConfig . addImport ( "parseFloat" , new MethodStub ( TbUtils . class . getMethod ( "parseFloat" ,
String . class ) ) ) ;
parserConfig . addImport ( "parseDouble" , new MethodStub ( TbUtils . class . getMethod ( "parseDouble" ,
@ -81,8 +89,58 @@ public class TbUtils {
byte [ ] . class , int . class , int . class ) ) ) ;
parserConfig . addImport ( "parseBytesToInt" , new MethodStub ( TbUtils . class . getMethod ( "parseBytesToInt" ,
byte [ ] . class , int . class , int . class , boolean . class ) ) ) ;
parserConfig . addImport ( "parseLittleEndianHexToLong" , new MethodStub ( TbUtils . class . getMethod ( "parseLittleEndianHexToLong" ,
String . class ) ) ) ;
parserConfig . addImport ( "parseBigEndianHexToLong" , new MethodStub ( TbUtils . class . getMethod ( "parseBigEndianHexToLong" ,
String . class ) ) ) ;
parserConfig . addImport ( "parseHexToLong" , new MethodStub ( TbUtils . class . getMethod ( "parseHexToLong" ,
String . class ) ) ) ;
parserConfig . addImport ( "parseHexToLong" , new MethodStub ( TbUtils . class . getMethod ( "parseHexToLong" ,
String . class , boolean . class ) ) ) ;
parserConfig . addImport ( "parseBytesToLong" , new MethodStub ( TbUtils . class . getMethod ( "parseBytesToLong" ,
List . class , int . class , int . class ) ) ) ;
parserConfig . addImport ( "parseBytesToLong" , new MethodStub ( TbUtils . class . getMethod ( "parseBytesToLong" ,
List . class , int . class , int . class , boolean . class ) ) ) ;
parserConfig . addImport ( "parseBytesToLong" , new MethodStub ( TbUtils . class . getMethod ( "parseBytesToLong" ,
byte [ ] . class , int . class , int . class ) ) ) ;
parserConfig . addImport ( "parseBytesToLong" , new MethodStub ( TbUtils . class . getMethod ( "parseBytesToLong" ,
byte [ ] . class , int . class , int . class , boolean . class ) ) ) ;
parserConfig . addImport ( "parseLittleEndianHexToFloat" , new MethodStub ( TbUtils . class . getMethod ( "parseLittleEndianHexToFloat" ,
String . class ) ) ) ;
parserConfig . addImport ( "parseBigEndianHexToFloat" , new MethodStub ( TbUtils . class . getMethod ( "parseBigEndianHexToFloat" ,
String . class ) ) ) ;
parserConfig . addImport ( "parseHexToFloat" , new MethodStub ( TbUtils . class . getMethod ( "parseHexToFloat" ,
String . class ) ) ) ;
parserConfig . addImport ( "parseHexToFloat" , new MethodStub ( TbUtils . class . getMethod ( "parseHexToFloat" ,
String . class , boolean . class ) ) ) ;
parserConfig . addImport ( "parseBytesToFloat" , new MethodStub ( TbUtils . class . getMethod ( "parseBytesToFloat" ,
byte [ ] . class , int . class , boolean . class ) ) ) ;
parserConfig . addImport ( "parseBytesToFloat" , new MethodStub ( TbUtils . class . getMethod ( "parseBytesToFloat" ,
byte [ ] . class , int . class ) ) ) ;
parserConfig . addImport ( "parseBytesToFloat" , new MethodStub ( TbUtils . class . getMethod ( "parseBytesToFloat" ,
List . class , int . class , boolean . class ) ) ) ;
parserConfig . addImport ( "parseBytesToFloat" , new MethodStub ( TbUtils . class . getMethod ( "parseBytesToFloat" ,
List . class , int . class ) ) ) ;
parserConfig . addImport ( "parseLittleEndianHexToDouble" , new MethodStub ( TbUtils . class . getMethod ( "parseLittleEndianHexToDouble" ,
String . class ) ) ) ;
parserConfig . addImport ( "parseBigEndianHexToDouble" , new MethodStub ( TbUtils . class . getMethod ( "parseBigEndianHexToDouble" ,
String . class ) ) ) ;
parserConfig . addImport ( "parseHexToDouble" , new MethodStub ( TbUtils . class . getMethod ( "parseHexToDouble" ,
String . class ) ) ) ;
parserConfig . addImport ( "parseHexToDouble" , new MethodStub ( TbUtils . class . getMethod ( "parseHexToDouble" ,
String . class , boolean . class ) ) ) ;
parserConfig . addImport ( "parseBytesToDouble" , new MethodStub ( TbUtils . class . getMethod ( "parseBytesToDouble" ,
byte [ ] . class , int . class ) ) ) ;
parserConfig . addImport ( "parseBytesToDouble" , new MethodStub ( TbUtils . class . getMethod ( "parseBytesToDouble" ,
byte [ ] . class , int . class , boolean . class ) ) ) ;
parserConfig . addImport ( "parseBytesToDouble" , new MethodStub ( TbUtils . class . getMethod ( "parseBytesToDouble" ,
List . class , int . class ) ) ) ;
parserConfig . addImport ( "parseBytesToDouble" , new MethodStub ( TbUtils . class . getMethod ( "parseBytesToDouble" ,
List . class , int . class , boolean . class ) ) ) ;
parserConfig . addImport ( "toFixed" , new MethodStub ( TbUtils . class . getMethod ( "toFixed" ,
double . class , int . class ) ) ) ;
parserConfig . addImport ( "toFixed" , new MethodStub ( TbUtils . class . getMethod ( "toFixed" ,
float . class , int . class ) ) ) ;
parserConfig . addImport ( "hexToBytes" , new MethodStub ( TbUtils . class . getMethod ( "hexToBytes" ,
ExecutionContext . class , String . class ) ) ) ;
parserConfig . addImport ( "base64ToHex" , new MethodStub ( TbUtils . class . getMethod ( "base64ToHex" ,
@ -154,37 +212,73 @@ public class TbUtils {
}
public static Integer parseInt ( String value ) {
if ( value ! = null ) {
int radix = getRadix ( value ) ;
return parseInt ( value , radix ) ;
}
public static Integer parseInt ( String value , int radix ) {
if ( StringUtils . isNotBlank ( value ) ) {
try {
int radix = 10 ;
if ( isHexadecimal ( value ) ) {
radix = 16 ;
String valueP = prepareNumberString ( value ) ;
isValidRadix ( valueP , radix ) ;
try {
return Integer . parseInt ( valueP , radix ) ;
} catch ( NumberFormatException e ) {
BigInteger bi = new BigInteger ( valueP , radix ) ;
if ( bi . compareTo ( BigInteger . valueOf ( Integer . MAX_VALUE ) ) > 0 )
throw new NumberFormatException ( "Value \"" + value + "\" is greater than the maximum Integer value " + Integer . MAX_VALUE + " !" ) ;
if ( bi . compareTo ( BigInteger . valueOf ( Integer . MIN_VALUE ) ) < 0 )
throw new NumberFormatException ( "Value \"" + value + "\" is less than the minimum Integer value " + Integer . MIN_VALUE + " !" ) ;
Float f = parseFloat ( valueP ) ;
if ( f ! = null ) {
return f . intValue ( ) ;
} else {
throw new NumberFormatException ( e . getMessage ( ) ) ;
}
}
return Integer . parseInt ( prepareNumberString ( value ) , radix ) ;
} catch ( NumberFormatException e ) {
Float f = parseFloat ( value ) ;
if ( f ! = null ) {
return f . intValue ( ) ;
}
throw new NumberFormatException ( e . getMessage ( ) ) ;
}
}
return null ;
}
public static Integer parseInt ( String value , int radix ) {
if ( value ! = null ) {
public static Long parseLong ( String value ) {
int radix = getRadix ( value ) ;
return parseLong ( value , radix ) ;
}
public static Long parseLong ( String value , int radix ) {
if ( StringUtils . isNotBlank ( value ) ) {
try {
return Integer . parseInt ( prepareNumberString ( value ) , radix ) ;
} catch ( NumberFormatException e ) {
Float f = parseFloat ( value ) ;
if ( f ! = null ) {
return f . intValue ( ) ;
String valueP = prepareNumberString ( value ) ;
isValidRadix ( valueP , radix ) ;
try {
return Long . parseLong ( valueP , radix ) ;
} catch ( NumberFormatException e ) {
BigInteger bi = new BigInteger ( valueP , radix ) ;
if ( bi . compareTo ( BigInteger . valueOf ( Long . MAX_VALUE ) ) > 0 )
throw new NumberFormatException ( "Value \"" + value + "\"is greater than the maximum Long value " + Long . MAX_VALUE + " !" ) ;
if ( bi . compareTo ( BigInteger . valueOf ( Long . MIN_VALUE ) ) < 0 )
throw new NumberFormatException ( "Value \"" + value + "\" is less than the minimum Long value " + Long . MIN_VALUE + " !" ) ;
Double dd = parseDouble ( valueP ) ;
if ( dd ! = null ) {
return dd . longValue ( ) ;
} else {
throw new NumberFormatException ( e . getMessage ( ) ) ;
}
}
} catch ( NumberFormatException e ) {
throw new NumberFormatException ( e . getMessage ( ) ) ;
}
}
return null ;
}
private static int getRadix ( String value , int . . . radixS ) {
return radixS . length > 0 ? radixS [ 0 ] : isHexadecimal ( value ) ? 16 : 10 ;
}
public static Float parseFloat ( String value ) {
if ( value ! = null ) {
try {
@ -218,8 +312,64 @@ public class TbUtils {
}
public static int parseHexToInt ( String hex , boolean bigEndian ) {
byte [ ] data = prepareHexToBytesNumber ( hex , 8 ) ;
return parseBytesToInt ( data , 0 , data . length , bigEndian ) ;
}
public static long parseLittleEndianHexToLong ( String hex ) {
return parseHexToLong ( hex , false ) ;
}
public static long parseBigEndianHexToLong ( String hex ) {
return parseHexToLong ( hex , true ) ;
}
public static long parseHexToLong ( String hex ) {
return parseHexToLong ( hex , true ) ;
}
public static long parseHexToLong ( String hex , boolean bigEndian ) {
byte [ ] data = prepareHexToBytesNumber ( hex , 16 ) ;
return parseBytesToLong ( data , 0 , data . length , bigEndian ) ;
}
public static float parseLittleEndianHexToFloat ( String hex ) {
return parseHexToFloat ( hex , false ) ;
}
public static float parseBigEndianHexToFloat ( String hex ) {
return parseHexToFloat ( hex , true ) ;
}
public static float parseHexToFloat ( String hex ) {
return parseHexToFloat ( hex , true ) ;
}
public static float parseHexToFloat ( String hex , boolean bigEndian ) {
byte [ ] data = prepareHexToBytesNumber ( hex , 8 ) ;
return parseBytesToFloat ( data , 0 , bigEndian ) ;
}
public static double parseLittleEndianHexToDouble ( String hex ) {
return parseHexToDouble ( hex , false ) ;
}
public static double parseBigEndianHexToDouble ( String hex ) {
return parseHexToDouble ( hex , true ) ;
}
public static double parseHexToDouble ( String hex ) {
return parseHexToDouble ( hex , true ) ;
}
public static double parseHexToDouble ( String hex , boolean bigEndian ) {
byte [ ] data = prepareHexToBytesNumber ( hex , 16 ) ;
return parseBytesToDouble ( data , 0 , bigEndian ) ;
}
private static byte [ ] prepareHexToBytesNumber ( String hex , int len ) {
int length = hex . length ( ) ;
if ( length > 8 ) {
if ( length > len ) {
throw new IllegalArgumentException ( "Hex string is too large. Maximum 8 symbols allowed." ) ;
}
if ( length % 2 > 0 ) {
@ -229,7 +379,7 @@ public class TbUtils {
for ( int i = 0 ; i < length ; i + = 2 ) {
data [ i / 2 ] = ( byte ) ( ( Character . digit ( hex . charAt ( i ) , 16 ) < < 4 ) + Character . digit ( hex . charAt ( i + 1 ) , 16 ) ) ;
}
return parseBytesToInt ( data , 0 , data . length , bigEndian ) ;
return data ;
}
public static ExecutionArrayList < Byte > hexToBytes ( ExecutionContext ctx , String hex ) {
@ -293,6 +443,91 @@ public class TbUtils {
return bb . getInt ( ) ;
}
public static long parseBytesToLong ( List < Byte > data , int offset , int length ) {
return parseBytesToLong ( data , offset , length , true ) ;
}
public static long parseBytesToLong ( List < Byte > data , int offset , int length , boolean bigEndian ) {
final byte [ ] bytes = new byte [ data . size ( ) ] ;
for ( int i = 0 ; i < bytes . length ; i + + ) {
bytes [ i ] = data . get ( i ) ;
}
return parseBytesToLong ( bytes , offset , length , bigEndian ) ;
}
public static long parseBytesToLong ( byte [ ] data , int offset , int length ) {
return parseBytesToLong ( data , offset , length , true ) ;
}
public static long parseBytesToLong ( byte [ ] data , int offset , int length , boolean bigEndian ) {
if ( offset > data . length ) {
throw new IllegalArgumentException ( "Offset: " + offset + " is out of bounds for array with length: " + data . length + "!" ) ;
}
if ( length > 8 ) {
throw new IllegalArgumentException ( "Length: " + length + " is too large. Maximum 4 bytes is allowed!" ) ;
}
if ( offset + length > data . length ) {
throw new IllegalArgumentException ( "Offset: " + offset + " and Length: " + length + " is out of bounds for array with length: " + data . length + "!" ) ;
}
var bb = ByteBuffer . allocate ( 8 ) ;
if ( ! bigEndian ) {
bb . order ( ByteOrder . LITTLE_ENDIAN ) ;
}
bb . position ( bigEndian ? 8 - length : 0 ) ;
bb . put ( data , offset , length ) ;
bb . position ( 0 ) ;
return bb . getLong ( ) ;
}
public static float parseBytesToFloat ( byte [ ] data , int offset ) {
return parseBytesToFloat ( data , offset , true ) ;
}
public static float parseBytesToFloat ( List data , int offset ) {
return parseBytesToFloat ( data , offset , true ) ;
}
public static float parseBytesToFloat ( List data , int offset , boolean bigEndian ) {
return parseBytesToFloat ( Bytes . toArray ( data ) , offset , bigEndian ) ;
}
public static float parseBytesToFloat ( byte [ ] data , int offset , boolean bigEndian ) {
byte [ ] bytesToNumber = prepareBytesToNumber ( data , offset , 4 , bigEndian ) ;
return ByteBuffer . wrap ( bytesToNumber ) . getFloat ( ) ;
}
public static double parseBytesToDouble ( byte [ ] data , int offset ) {
return parseBytesToDouble ( data , offset , true ) ;
}
public static double parseBytesToDouble ( List data , int offset ) {
return parseBytesToDouble ( data , offset , true ) ;
}
public static double parseBytesToDouble ( List data , int offset , boolean bigEndian ) {
return parseBytesToDouble ( Bytes . toArray ( data ) , offset , bigEndian ) ;
}
public static double parseBytesToDouble ( byte [ ] data , int offset , boolean bigEndian ) {
byte [ ] bytesToNumber = prepareBytesToNumber ( data , offset , 8 , bigEndian ) ;
return ByteBuffer . wrap ( bytesToNumber ) . getDouble ( ) ;
}
private static byte [ ] prepareBytesToNumber ( byte [ ] data , int offset , int length , boolean bigEndian ) {
if ( offset > data . length ) {
throw new IllegalArgumentException ( "Offset: " + offset + " is out of bounds for array with length: " + data . length + "!" ) ;
}
if ( ( offset + length ) > data . length ) {
throw new IllegalArgumentException ( "Default length is always " + length + " bytes. Offset: " + offset + " and Length: " + length + " is out of bounds for array with length: " + data . length + "!" ) ;
}
byte [ ] dataBytesArray = Arrays . copyOfRange ( data , offset , ( offset + length ) ) ;
if ( ! bigEndian ) {
ArrayUtils . reverse ( dataBytesArray ) ;
}
return dataBytesArray ;
}
public static String bytesToHex ( ExecutionArrayList < ? > bytesList ) {
byte [ ] bytes = new byte [ bytesList . size ( ) ] ;
for ( int i = 0 ; i < bytesList . size ( ) ; i + + ) {
@ -315,6 +550,10 @@ public class TbUtils {
return BigDecimal . valueOf ( value ) . setScale ( precision , RoundingMode . HALF_UP ) . doubleValue ( ) ;
}
public static float toFixed ( float value , int precision ) {
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" ) ) ;
}
@ -388,4 +627,19 @@ public class TbUtils {
}
}
}
public static boolean isValidRadix ( String value , int radix ) {
for ( int i = 0 ; i < value . length ( ) ; i + + ) {
if ( i = = 0 & & value . charAt ( i ) = = '-' ) {
if ( value . length ( ) = = 1 )
throw new NumberFormatException ( "Failed radix [" + radix + "] for value: \"" + value + "\"!" ) ;
else
continue ;
}
if ( Character . digit ( value . charAt ( i ) , radix ) < 0 )
throw new NumberFormatException ( "Failed radix: [" + radix + "] for value: \"" + value + "\"!" ) ;
}
return true ;
}
}