Skip to content

Commit 6cba357

Browse files
committed
some optimizations
1 parent f4d38f1 commit 6cba357

File tree

7 files changed

+210
-142
lines changed

7 files changed

+210
-142
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ plugins {
33
}
44

55
group = 'com.github.dedinc'
6-
version = '1.0'
6+
version = '1.12'
77

88
repositories {
99
mavenCentral()

src/main/java/com/github/dedinc/jsonfixer4j/JSONAutoCorrector.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import java.util.LinkedHashMap;
44
import java.util.List;
55

6-
public class JSONAutoCorrector {
6+
public final class JSONAutoCorrector {
77

88
private final JSONTokenizer tokenizer;
99
private final JSONParser parser;
@@ -26,5 +26,3 @@ public String autocorrect(String s) {
2626
return serializer.serialize(result);
2727
}
2828
}
29-
30-

src/main/java/com/github/dedinc/jsonfixer4j/JSONParser.java

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
import java.util.List;
66
import java.util.Map;
77

8-
class JSONParser {
8+
public final class JSONParser {
99

10-
static class ParseResult {
11-
Object value;
12-
int index;
10+
public static class ParseResult {
11+
public final Object value;
12+
public final int index;
1313

1414
public ParseResult(Object value, int index) {
1515
this.value = value;
@@ -24,14 +24,15 @@ public ParseResult parse(List<Token> tokenList, int idx) {
2424
Token token = tokenList.get(idx);
2525
switch (token.kind) {
2626
case LBRACE:
27-
return _parseObject(tokenList, idx + 1);
27+
return parseObject(tokenList, idx + 1);
2828
case LBRACKET:
29-
return _parseArray(tokenList, idx + 1);
29+
return parseArray(tokenList, idx + 1);
3030
case STRING:
3131
return new ParseResult(token.value, idx + 1);
3232
case NUMBER:
3333
try {
34-
if (token.value.contains(".")) {
34+
35+
if (token.value.indexOf('.') != -1) {
3536
return new ParseResult(Double.parseDouble(token.value), idx + 1);
3637
} else {
3738
return new ParseResult(Integer.parseInt(token.value), idx + 1);
@@ -54,20 +55,15 @@ public ParseResult parse(List<Token> tokenList, int idx) {
5455
}
5556
}
5657

57-
private ParseResult _parseObject(List<Token> tokenList, int idx) {
58+
private ParseResult parseObject(List<Token> tokenList, int idx) {
5859
Map<String, Object> result = new LinkedHashMap<>();
5960
boolean expectComma = false;
60-
while (true) {
61-
if (idx >= tokenList.size()) {
62-
return new ParseResult(result, idx);
63-
}
61+
final int size = tokenList.size();
62+
while (idx < size) {
6463
Token token = tokenList.get(idx);
65-
if (token.kind == TokenKind.RBRACE) {
64+
if (token.kind == TokenKind.RBRACE || token.kind == TokenKind.EOF) {
6665
return new ParseResult(result, idx + 1);
6766
}
68-
if (token.kind == TokenKind.EOF) {
69-
return new ParseResult(result, idx);
70-
}
7167
if (expectComma) {
7268
if (token.kind == TokenKind.COMMA) {
7369
idx++;
@@ -81,37 +77,31 @@ private ParseResult _parseObject(List<Token> tokenList, int idx) {
8177
if (token.kind == TokenKind.STRING) {
8278
String key = token.value;
8379
idx++;
84-
if (idx < tokenList.size() && tokenList.get(idx).kind == TokenKind.COLON) {
80+
if (idx < size && tokenList.get(idx).kind == TokenKind.COLON) {
8581
idx++;
8682
ParseResult pr = parse(tokenList, idx);
8783
result.put(key, pr.value);
8884
idx = pr.index;
8985
expectComma = true;
9086
} else {
91-
9287
idx++;
93-
continue;
9488
}
9589
} else {
9690
idx++;
9791
}
9892
}
93+
return new ParseResult(result, idx);
9994
}
10095

101-
private ParseResult _parseArray(List<Token> tokenList, int idx) {
96+
private ParseResult parseArray(List<Token> tokenList, int idx) {
10297
List<Object> result = new ArrayList<>();
10398
boolean expectComma = false;
104-
while (true) {
105-
if (idx >= tokenList.size()) {
106-
return new ParseResult(result, idx);
107-
}
99+
final int size = tokenList.size();
100+
while (idx < size) {
108101
Token token = tokenList.get(idx);
109-
if (token.kind == TokenKind.RBRACKET) {
102+
if (token.kind == TokenKind.RBRACKET || token.kind == TokenKind.EOF) {
110103
return new ParseResult(result, idx + 1);
111104
}
112-
if (token.kind == TokenKind.EOF) {
113-
return new ParseResult(result, idx);
114-
}
115105
if (expectComma) {
116106
if (token.kind == TokenKind.COMMA) {
117107
idx++;
@@ -124,17 +114,17 @@ private ParseResult _parseArray(List<Token> tokenList, int idx) {
124114
continue;
125115
}
126116
}
127-
128-
if (!(token.kind == TokenKind.LBRACE || token.kind == TokenKind.LBRACKET ||
129-
token.kind == TokenKind.STRING || token.kind == TokenKind.NUMBER ||
130-
token.kind == TokenKind.TRUE || token.kind == TokenKind.FALSE ||
131-
token.kind == TokenKind.NULL)) {
117+
if (token.kind != TokenKind.LBRACE && token.kind != TokenKind.LBRACKET &&
118+
token.kind != TokenKind.STRING && token.kind != TokenKind.NUMBER &&
119+
token.kind != TokenKind.TRUE && token.kind != TokenKind.FALSE &&
120+
token.kind != TokenKind.NULL) {
132121
return new ParseResult(result, idx);
133122
}
134123
ParseResult pr = parse(tokenList, idx);
135124
result.add(pr.value);
136125
idx = pr.index;
137126
expectComma = true;
138127
}
128+
return new ParseResult(result, idx);
139129
}
140130
}
Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,97 @@
11
package com.github.dedinc.jsonfixer4j;
22

3-
import java.util.ArrayList;
43
import java.util.List;
54
import java.util.Map;
65

7-
class JSONSerializer {
6+
public final class JSONSerializer {
7+
8+
private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
89

910
public String serialize(Object obj) {
10-
if (Boolean.TRUE.equals(obj)) {
11-
return "true";
12-
} else if (Boolean.FALSE.equals(obj)) {
13-
return "false";
14-
} else if (obj == null) {
15-
return "null";
16-
} else if (obj instanceof Number) {
17-
return obj.toString();
11+
12+
StringBuilder sb = new StringBuilder(256);
13+
serialize(obj, sb);
14+
return sb.toString();
15+
}
16+
17+
private void serialize(Object obj, StringBuilder sb) {
18+
if (obj == null) {
19+
sb.append("null");
20+
} else if (obj instanceof Boolean || obj instanceof Number) {
21+
sb.append(obj.toString());
1822
} else if (obj instanceof String) {
19-
String safeStr = ((String) obj).replace("\\", "\\\\").replace("\"", "\\\"");
20-
return "\"" + safeStr + "\"";
23+
appendEscapedString((String) obj, sb);
2124
} else if (obj instanceof List) {
22-
@SuppressWarnings("unchecked")
23-
List<Object> list = (List<Object>) obj;
24-
List<String> parts = new ArrayList<>();
25-
for (Object o : list) {
26-
parts.add(serialize(o));
25+
List<?> list = (List<?>) obj;
26+
sb.append('[');
27+
int size = list.size();
28+
for (int i = 0; i < size; i++) {
29+
if (i > 0) {
30+
sb.append(", ");
31+
}
32+
serialize(list.get(i), sb);
2733
}
28-
return "[" + String.join(", ", parts) + "]";
34+
sb.append(']');
2935
} else if (obj instanceof Map) {
3036
@SuppressWarnings("unchecked")
3137
Map<Object, Object> map = (Map<Object, Object>) obj;
32-
List<String> items = new ArrayList<>();
38+
sb.append('{');
39+
boolean first = true;
3340
for (Map.Entry<Object, Object> entry : map.entrySet()) {
34-
String keyStr = serialize(entry.getKey());
35-
String valueStr = serialize(entry.getValue());
36-
items.add(keyStr + ": " + valueStr);
41+
if (!first) {
42+
sb.append(", ");
43+
}
44+
first = false;
45+
serialize(entry.getKey(), sb);
46+
sb.append(": ");
47+
serialize(entry.getValue(), sb);
3748
}
38-
return "{" + String.join(", ", items) + "}";
49+
sb.append('}');
3950
} else {
40-
return "\"" + obj + "\"";
51+
appendEscapedString(obj.toString(), sb);
52+
}
53+
}
54+
55+
private void appendEscapedString(String str, StringBuilder sb) {
56+
sb.append('"');
57+
final int len = str.length();
58+
for (int i = 0; i < len; i++) {
59+
char ch = str.charAt(i);
60+
switch (ch) {
61+
case '\\':
62+
sb.append("\\\\");
63+
break;
64+
case '"':
65+
sb.append("\\\"");
66+
break;
67+
case '\b':
68+
sb.append("\\b");
69+
break;
70+
case '\f':
71+
sb.append("\\f");
72+
break;
73+
case '\n':
74+
sb.append("\\n");
75+
break;
76+
case '\r':
77+
sb.append("\\r");
78+
break;
79+
case '\t':
80+
sb.append("\\t");
81+
break;
82+
default:
83+
if (ch < 0x20) {
84+
sb.append("\\u");
85+
sb.append(HEX_DIGITS[(ch >> 12) & 0xF]);
86+
sb.append(HEX_DIGITS[(ch >> 8) & 0xF]);
87+
sb.append(HEX_DIGITS[(ch >> 4) & 0xF]);
88+
sb.append(HEX_DIGITS[ch & 0xF]);
89+
} else {
90+
sb.append(ch);
91+
}
92+
break;
93+
}
4194
}
95+
sb.append('"');
4296
}
4397
}
Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,54 @@
11
package com.github.dedinc.jsonfixer4j;
22

3-
import java.util.*;
3+
import java.util.ArrayDeque;
4+
import java.util.ArrayList;
5+
import java.util.Deque;
6+
import java.util.List;
47

5-
class JSONTokenFixer {
8+
public final class JSONTokenFixer {
69

710
public static List<Token> fixTokens(List<Token> tokens) {
8-
List<Token> fixed = new ArrayList<>();
9-
Deque<TokenKind> stack = new ArrayDeque<>();
10-
Map<TokenKind, TokenKind> matching = new HashMap<>();
11-
matching.put(TokenKind.LBRACE, TokenKind.RBRACE);
12-
matching.put(TokenKind.LBRACKET, TokenKind.RBRACKET);
13-
11+
List<Token> fixed = new ArrayList<>(tokens.size());
12+
Deque<TokenKind> stack = new ArrayDeque<>(tokens.size());
1413
for (Token token : tokens) {
15-
if (token.kind == TokenKind.LBRACE || token.kind == TokenKind.LBRACKET) {
16-
fixed.add(token);
17-
stack.push(matching.get(token.kind));
18-
} else if (token.kind == TokenKind.RBRACE || token.kind == TokenKind.RBRACKET) {
19-
if (!stack.isEmpty()) {
20-
TokenKind expected = stack.peek();
21-
if (token.kind == expected) {
14+
switch (token.kind) {
15+
case LBRACE:
16+
case LBRACKET:
17+
fixed.add(token);
18+
stack.push(token.kind == TokenKind.LBRACE ? TokenKind.RBRACE : TokenKind.RBRACKET);
19+
break;
20+
case RBRACE:
21+
case RBRACKET:
22+
if (!stack.isEmpty() && token.kind == stack.peek()) {
2223
stack.pop();
2324
fixed.add(token);
2425
} else {
25-
26-
if (expected == TokenKind.RBRACKET) {
27-
fixed.add(new Token(TokenKind.RBRACKET, "]"));
26+
TokenKind expectedToken = stack.isEmpty() ? null : stack.peek();
27+
fixed.add(createMatchingToken(expectedToken, token));
28+
if (!stack.isEmpty() && token.kind == stack.peek()) {
2829
stack.pop();
29-
if (!stack.isEmpty() && token.kind == stack.peek()) {
30-
stack.pop();
31-
fixed.add(token);
32-
} else {
33-
fixed.add(token);
34-
}
35-
} else {
3630
fixed.add(token);
3731
}
3832
}
39-
} else {
33+
break;
34+
default:
4035
fixed.add(token);
41-
}
42-
} else {
43-
fixed.add(token);
36+
break;
4437
}
4538
}
4639
while (!stack.isEmpty()) {
47-
TokenKind exp = stack.pop();
48-
if (exp == TokenKind.RBRACE) {
49-
fixed.add(new Token(TokenKind.RBRACE, "}"));
50-
} else if (exp == TokenKind.RBRACKET) {
51-
fixed.add(new Token(TokenKind.RBRACKET, "]"));
52-
}
40+
TokenKind expectedToken = stack.pop();
41+
fixed.add(createMatchingToken(expectedToken, null));
5342
}
5443
return fixed;
5544
}
45+
46+
private static Token createMatchingToken(TokenKind expectedToken, Token originalToken) {
47+
if (expectedToken == TokenKind.RBRACE) {
48+
return new Token(TokenKind.RBRACE, "}");
49+
} else if (expectedToken == TokenKind.RBRACKET) {
50+
return new Token(TokenKind.RBRACKET, "]");
51+
}
52+
return originalToken;
53+
}
5654
}

0 commit comments

Comments
 (0)