Skip to content

Commit 9fb13e2

Browse files
committed
string performance optimisation
1 parent 0b40af3 commit 9fb13e2

File tree

7 files changed

+180
-148
lines changed

7 files changed

+180
-148
lines changed

src/main/java/com/dashjoin/jsonata/Functions.java

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -609,10 +609,11 @@ public static String leftPad(final String str, final int size, String padStr) {
609609
if (pads <= 0) {
610610
return str;
611611
}
612-
String padding = "";
612+
StringBuilder paddingSb = new StringBuilder();
613613
for (int i = 0; i < pads + 1; i++) {
614-
padding += padStr;
614+
paddingSb.append(padStr);
615615
}
616+
String padding = paddingSb.toString();
616617
return substr(padding, 0, pads).concat(str);
617618
}
618619

@@ -638,10 +639,11 @@ public static String rightPad(final String str, final int size, String padStr) {
638639
if (pads <= 0) {
639640
return str;
640641
}
641-
String padding = "";
642+
StringBuilder paddingSb = new StringBuilder();
642643
for (int i = 0; i < pads + 1; i++) {
643-
padding += padStr;
644+
paddingSb.append(padStr);
644645
}
646+
String padding = paddingSb.toString();
645647
return str.concat(substr(padding, 0, pads));
646648
}
647649

@@ -770,14 +772,21 @@ public static String join(List<String> strs, String separator) {
770772
return String.join(separator, strs);
771773
}
772774

775+
private static final Pattern DOLLAR_DOLLAR = Pattern.compile("\\$\\$");
776+
private static final Pattern DOLLAR_WITHOUT_ESCAPE = Pattern.compile("([^\\\\]|^)\\$([^0-9^<])");
777+
private static final Pattern DOLLAR_AT_END = Pattern.compile("\\$$");
773778
static String safeReplacement(String in) {
774779
// In JSONata and in Java the $ in the replacement test usually starts the insertion of a capturing group
775780
// In order to replace a simple $ in Java you have to escape the $ with "\$"
776781
// in JSONata you do this with a '$$'
777-
// "\$" followed any character besides '<' and and digit into $ + this character
778-
return in.replaceAll("\\$\\$", "\\\\\\$")
779-
.replaceAll("([^\\\\]|^)\\$([^0-9^<])", "$1\\\\\\$$2")
780-
.replaceAll("\\$$", "\\\\\\$"); // allow $ at end
782+
// "\$" followed any character besides '<' and and digit into $ + this character
783+
if (!in.contains("$")) {
784+
return in;
785+
}
786+
String result = DOLLAR_DOLLAR.matcher(in).replaceAll("\\\\\\$");
787+
result = DOLLAR_WITHOUT_ESCAPE.matcher(result).replaceAll("$1\\\\\\$$2");
788+
result = DOLLAR_AT_END.matcher(result).replaceAll("\\\\\\$");
789+
return result;
781790
}
782791

783792
/**
@@ -813,7 +822,7 @@ static String safeReplaceAll(String s, Pattern pattern, Object _replacement) {
813822
if (!msg.contains("No group")) throw e;
814823

815824
// Adjust replacement to remove the non-existing group
816-
String g = "" + msg.charAt(msg.length()-1);
825+
String g = String.valueOf(msg.charAt(msg.length()-1));
817826

818827
replacement = replacement.replace("$"+g, "");
819828
}
@@ -865,7 +874,7 @@ static String safeReplaceAllFn(String s, Pattern pattern, Object fn) {
865874

866875
/**
867876
* Safe replaceFirst
868-
*
877+
*
869878
* @param s
870879
* @param pattern
871880
* @param replacement
@@ -887,7 +896,7 @@ static String safeReplaceFirst(String s, Pattern pattern, String replacement) {
887896
if (!msg.contains("No group")) throw e;
888897

889898
// Adjust replacement to remove the non-existing group
890-
String g = "" + msg.charAt(msg.length()-1);
899+
String g = String.valueOf(msg.charAt(msg.length()-1));
891900

892901
replacement = replacement.replace("$"+g, "");
893902
}
@@ -909,10 +918,10 @@ public static String replace(String str, Object pattern, Object replacement, Int
909918
return safeReplaceAll(str, (Pattern)pattern, replacement);
910919
}
911920
} else {
912-
921+
913922
if (limit<0)
914923
throw new JException("Fourth argument of replace function must evaluate to a positive number", 0);
915-
924+
916925
for (int i=0; i<limit; i++)
917926
if (pattern instanceof String) {
918927
str = str.replaceFirst((String)pattern, (String)replacement);
@@ -963,6 +972,12 @@ public static String base64decode(String str) {
963972
}
964973
}
965974

975+
private static final Pattern PLUS = Pattern.compile("\\+");
976+
private static final Pattern PERCENT_21 = Pattern.compile("%21");
977+
private static final Pattern PERCENT_27 = Pattern.compile("%27");
978+
private static final Pattern PERCENT_28 = Pattern.compile("%28");
979+
private static final Pattern PERCENT_29 = Pattern.compile("%29");
980+
private static final Pattern PERCENT_7E = Pattern.compile("%7E");
966981
/**
967982
* Encode a string into a component for a url
968983
* @param {String} str - String to encode
@@ -975,14 +990,20 @@ public static String encodeUrlComponent(String str) {
975990
}
976991

977992
Utils.checkUrl(str);
978-
979-
return URLEncoder.encode(str, StandardCharsets.UTF_8)
980-
.replaceAll("\\+", "%20")
981-
.replaceAll("\\%21", "!")
982-
.replaceAll("\\%27", "'")
983-
.replaceAll("\\%28", "(")
984-
.replaceAll("\\%29", ")")
985-
.replaceAll("\\%7E", "~");
993+
994+
String encoded = URLEncoder.encode(str, StandardCharsets.UTF_8);
995+
996+
if (!encoded.contains("+") && !encoded.contains("%")) {
997+
return encoded;
998+
}
999+
1000+
encoded = PLUS.matcher(encoded).replaceAll("%20");
1001+
encoded = PERCENT_21.matcher(encoded).replaceAll("!");
1002+
encoded = PERCENT_27.matcher(encoded).replaceAll("'");
1003+
encoded = PERCENT_28.matcher(encoded).replaceAll("(");
1004+
encoded = PERCENT_29.matcher(encoded).replaceAll(")");
1005+
encoded = PERCENT_7E.matcher(encoded).replaceAll("~");
1006+
return encoded;
9861007
}
9871008

9881009
/**
@@ -997,7 +1018,7 @@ public static String encodeUrl(String str) {
9971018
}
9981019

9991020
Utils.checkUrl(str);
1000-
1021+
10011022
try {
10021023
// only encode query part: https://docs.jsonata.org/string-functions#encodeurl
10031024
URL url = new URL(str);
@@ -1083,7 +1104,7 @@ public static List<String> split(String str, Object pattern, Number limit) {
10831104
// $split("str", ""): Split string into characters
10841105
int l = limit!=null ? limit.intValue() : Integer.MAX_VALUE;
10851106
for (int i=0; i<str.length() && i<l; i++) {
1086-
result.add( ""+str.charAt(i) );
1107+
result.add(String.valueOf(str.charAt(i)));
10871108
}
10881109
} else {
10891110
// Quote separator string + preserve trailing empty strings (-1)

0 commit comments

Comments
 (0)