Skip to content

Commit 267a13d

Browse files
aeberhartsergeevik
authored andcommitted
Merge pull request #84 from sergeevik/fix/Functions_getFunction
fix: get a function from a class that is passed as a call attribute
2 parents 10c3f2c + ab5086c commit 267a13d

File tree

7 files changed

+120
-88
lines changed

7 files changed

+120
-88
lines changed

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

Lines changed: 41 additions & 20 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
}
@@ -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
}
@@ -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
/**
@@ -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)
@@ -2142,7 +2163,7 @@ public static String test(String a, String b) {
21422163
}
21432164

21442165
public static Method getFunction(Class clz, String name) {
2145-
Method[] methods = Functions.class.getMethods();
2166+
Method[] methods = clz.getMethods();
21462167
for (Method m : methods) {
21472168
// if (m.getModifiers() == (Modifier.STATIC | Modifier.PUBLIC) ) {
21482169
// System.out.println(m.getName());

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ Frame createFrameFromTuple(Frame environment, Map<String, Object> tuple) {
553553
Infix expr = (Infix)_expr;
554554
Object result = null;
555555
var lhs = /* await */ evaluate(expr.lhs, input, environment);
556-
String op = ""+expr.value;
556+
String op = String.valueOf(expr.value);
557557

558558
if (op.equals("and") || op.equals("or")) {
559559

@@ -627,7 +627,7 @@ public Object call() throws Exception {
627627
/* async */ Object evaluateUnary(Symbol expr, Object input, Frame environment) {
628628
Object result = null;
629629

630-
switch ((String)""+expr.value) { // Uli was: expr.value - where is value set???
630+
switch (String.valueOf(expr.value)) { // Uli was: expr.value - where is value set???
631631
case "-":
632632
result = /* await */ evaluate(expr.expression, input, environment);
633633
if (result==null) { //(typeof result === "undefined") {
@@ -652,7 +652,7 @@ public Object call() throws Exception {
652652
environment.isParallelCall = idx > 0;
653653
Object value = evaluate(item, input, environment);
654654
if (value!=null) {
655-
if ((""+item.value).equals("["))
655+
if ((String.valueOf(item.value)).equals("["))
656656
((List)result).add(value);
657657
else
658658
result = Functions.append(result, value);
@@ -1219,7 +1219,7 @@ Object evaluateRangeExpression(Object lhs, Object rhs) {
12191219
// The RHS is the expression to evaluate
12201220
// The LHS is the name of the variable to bind to - should be a VARIABLE token (enforced by parser)
12211221
var value = /* await */ evaluate(expr.rhs, input, environment);
1222-
environment.bind(""+expr.lhs.value, value);
1222+
environment.bind(String.valueOf(expr.lhs.value), value);
12231223
return value;
12241224
}
12251225

@@ -1904,7 +1904,7 @@ Object validateArguments(Object signature, Object args, Object context) {
19041904
var env = createFrame(proc.environment);
19051905
for (int i=0; i<proc.arguments.size(); i++) {
19061906
if (i>=args.size()) break;
1907-
env.bind(""+proc.arguments.get(i).value, args.get(i));
1907+
env.bind(String.valueOf(proc.arguments.get(i).value), args.get(i));
19081908
}
19091909
if (proc.body instanceof Symbol) {
19101910
result = evaluate(proc.body, proc.input, env);

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ Symbol advance(String id, boolean infix) {
295295
symbol = symbolTable.get("(name)");
296296
break;
297297
case "operator":
298-
symbol = symbolTable.get(""+value);
298+
symbol = symbolTable.get(String.valueOf(value));
299299
if (symbol==null) {
300300
return handleError(new JException(
301301
"S0204", next_token.position, value));
@@ -590,17 +590,18 @@ Symbol led(Symbol left) {
590590
// is the next token a '<' - if so, parse the function signature
591591
if (node.id.equals("<")) {
592592
int depth = 1;
593-
String sig = "<";
593+
StringBuilder sigBuilder = new StringBuilder("<");
594594
while (depth > 0 && !node.id.equals("{") && !node.id.equals("(end)")) {
595595
Symbol tok = advance();
596596
if (tok.id.equals(">")) {
597597
depth--;
598598
} else if (tok.id.equals("<")) {
599599
depth++;
600600
}
601-
sig += tok.value;
601+
sigBuilder.append(tok.value);
602602
}
603603
advance(">");
604+
String sig = sigBuilder.toString();
604605
this.signature = new Signature(sig, "lambda");
605606
}
606607
// parse the function body
@@ -978,7 +979,7 @@ Symbol processAST(Symbol expr) {
978979
if (dbg) System.out.println(" > processAST type="+expr.type+" value='"+expr.value+"'");
979980
switch (expr.type != null ? expr.type : "(null)") {
980981
case "binary": {
981-
switch (""+expr.value) {
982+
switch (String.valueOf(expr.value)) {
982983
case ".":
983984
var lstep = processAST(((Infix)expr).lhs);
984985

@@ -1039,12 +1040,12 @@ Symbol processAST(Symbol expr) {
10391040
}
10401041
// if first step is a path constructor, flag it for special handling
10411042
var firststep = result.steps.get(0);
1042-
if (firststep.type.equals("unary") && (""+firststep.value).equals("[")) {
1043+
if (firststep.type.equals("unary") && (String.valueOf(firststep.value)).equals("[")) {
10431044
firststep.consarray = true;
10441045
}
10451046
// if the last step is an array constructor, flag it so it doesn't flatten
10461047
var laststep = result.steps.get(result.steps.size() - 1);
1047-
if (laststep.type.equals("unary") && (""+laststep.value).equals("[")) {
1048+
if (laststep.type.equals("unary") && (String.valueOf(laststep.value)).equals("[")) {
10481049
laststep.consarray = true;
10491050
}
10501051
resolveAncestry(result);
@@ -1228,7 +1229,7 @@ Symbol processAST(Symbol expr) {
12281229
result = new Symbol();
12291230
result.type = expr.type; result.value = expr.value; result.position = expr.position;
12301231
// expr.value might be Character!
1231-
String exprValue = ""+expr.value;
1232+
String exprValue = String.valueOf(expr.value);
12321233
if (exprValue.equals("[")) {
12331234
if (dbg) System.out.println("unary [ "+result);
12341235
// array constructor - process each item
@@ -1354,7 +1355,7 @@ Symbol processAST(Symbol expr) {
13541355
if (expr.value.equals("and") || expr.value.equals("or") || expr.value.equals("in")) {
13551356
expr.type = "name";
13561357
result = processAST(expr);
1357-
} else /* istanbul ignore else */ if ((""+expr.value).equals("?")) {
1358+
} else /* istanbul ignore else */ if ((String.valueOf(expr.value)).equals("?")) {
13581359
// partial application
13591360
result = expr;
13601361
} else {

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ public class Tokenizer { // = function (path) {
8383
put("t", "\t");
8484
}};
8585

86+
private static final Pattern NUM_REGEX = Pattern.compile("^-?(0|([1-9][0-9]*))(\\.[0-9]+)?([Ee][-+]?[0-9]+)?");
8687
// Tokenizer (lexer) - invoked by the parser to return one token at a time
8788
String path;
8889
int position = 0;
@@ -253,7 +254,7 @@ Token next(boolean prefix) {
253254
return create("operator", "??");
254255
}
255256
// test for single char operators
256-
if (operators.get(""+currentChar)!=null) {
257+
if (operators.get(String.valueOf(currentChar))!=null) {
257258
position++;
258259
return create("operator", currentChar);
259260
}
@@ -262,20 +263,20 @@ Token next(boolean prefix) {
262263
char quoteType = currentChar;
263264
// double quoted string literal - find end of string
264265
position++;
265-
var qstr = "";
266+
var qstr = new StringBuilder();
266267
while (position < length) {
267268
currentChar = path.charAt(position);
268269
if (currentChar == '\\') { // escape sequence
269270
position++;
270271
if (position < path.length()) currentChar = path.charAt(position); else throw new JException("S0103", position, "");
271-
if (escapes.get(""+currentChar)!=null) {
272-
qstr += escapes.get(""+currentChar);
272+
if (escapes.get(String.valueOf(currentChar))!=null) {
273+
qstr.append(escapes.get(String.valueOf(currentChar)));
273274
} else if (currentChar == 'u') {
274275
// u should be followed by 4 hex digits
275276
String octets = position+5 < path.length() ? path.substring(position + 1, (position + 1) + 4) : "";
276277
if (octets.matches("^[0-9a-fA-F]+$")) { // /^[0-9a-fA-F]+$/.test(octets)) {
277278
int codepoint = Integer.parseInt(octets, 16);
278-
qstr += Character.toString((char) codepoint);
279+
qstr.append((char) codepoint);
279280
position += 4;
280281
} else {
281282
throw new JException("S0104", position);
@@ -287,17 +288,16 @@ Token next(boolean prefix) {
287288
}
288289
} else if (currentChar == quoteType) {
289290
position++;
290-
return create("string", qstr);
291+
return create("string", qstr.toString());
291292
} else {
292-
qstr += currentChar;
293+
qstr.append(currentChar);
293294
}
294295
position++;
295296
}
296297
throw new JException("S0101", position);
297298
}
298299
// test for numbers
299-
Pattern numregex = Pattern.compile("^-?(0|([1-9][0-9]*))(\\.[0-9]+)?([Ee][-+]?[0-9]+)?");
300-
Matcher match = numregex.matcher(path.substring(position));
300+
Matcher match = NUM_REGEX.matcher(path.substring(position));
301301
if (match.find()) {
302302
double num = Double.parseDouble(match.group(0));
303303
if (!Double.isNaN(num) && Double.isFinite(num)) {
@@ -330,7 +330,7 @@ Token next(boolean prefix) {
330330
//if (i>=length) return null; // Uli: JS relies on charAt returns null
331331

332332
ch = i<length ? path.charAt(i) : 0;
333-
if (i == length || " \t\n\r".indexOf(ch) > -1 || operators.containsKey(""+ch)) { // Uli: removed \v
333+
if (i == length || " \t\n\r".indexOf(ch) > -1 || operators.containsKey(String.valueOf(ch))) { // Uli: removed \v
334334
if (path.charAt(position) == '$') {
335335
// variable reference
336336
String _name = path.substring(position + 1, i);

src/main/java/com/dashjoin/jsonata/json/JsonParser.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,22 +344,32 @@ private void readEscape() throws IOException {
344344
captureBuffer.append('\t');
345345
break;
346346
case 'u':
347-
char[] hexChars = new char[4];
347+
int value = 0;
348348
for (int i = 0; i < 4; i++) {
349349
read();
350350
if (!isHexDigit()) {
351351
throw expected("hexadecimal digit");
352352
}
353-
hexChars[i] = (char)current;
353+
value = (value << 4) | hexCharToValue((char) current);
354354
}
355-
captureBuffer.append((char)Integer.parseInt(new String(hexChars), 16));
355+
captureBuffer.append((char) value);
356356
break;
357357
default:
358358
throw expected("valid escape sequence");
359359
}
360360
read();
361361
}
362362

363+
private int hexCharToValue(char c) {
364+
if (c >= '0' && c <= '9') {
365+
return c - '0';
366+
} else if (c >= 'A' && c <= 'F') {
367+
return c - 'A' + 10;
368+
} else { // c >= 'a' && c <= 'f'
369+
return c - 'a' + 10;
370+
}
371+
}
372+
363373
private void readNumber() throws IOException {
364374
handler.startNumber();
365375
startCapture();

0 commit comments

Comments
 (0)