• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

javadev / underscore-java / #4211

04 Jun 2025 06:13PM CUT coverage: 100.0%. Remained the same
#4211

push

web-flow
Spring boot 3.5.0

4519 of 4519 relevant lines covered (100.0%)

1.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/src/main/java/com/github/underscore/Json.java
1
/*
2
 * The MIT License (MIT)
3
 *
4
 * Copyright 2015-2025 Valentyn Kolesnikov
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 */
24
package com.github.underscore;
25

26
import java.util.ArrayList;
27
import java.util.Collection;
28
import java.util.Iterator;
29
import java.util.LinkedHashMap;
30
import java.util.List;
31
import java.util.Map;
32

33
@SuppressWarnings({"java:S3740", "java:S3776"})
34
public final class Json {
35

36
    private static final int PARSE_MAX_DEPTH = 10_000;
37

38
    private Json() {}
39

40
    private static final String NULL = "null";
41
    private static final String DIGIT = "digit";
42

43
    public static class JsonStringBuilder {
44
        public enum Step {
1✔
45
            TWO_SPACES(2),
1✔
46
            THREE_SPACES(3),
1✔
47
            FOUR_SPACES(4),
1✔
48
            COMPACT(0),
1✔
49
            TABS(1);
1✔
50
            private final int indent;
51

52
            Step(int indent) {
1✔
53
                this.indent = indent;
1✔
54
            }
1✔
55

56
            public int getIndent() {
57
                return indent;
1✔
58
            }
59
        }
60

61
        private final StringBuilder builder;
62
        private final Step identStep;
63
        private int indent;
64

65
        public JsonStringBuilder(Step identStep) {
1✔
66
            builder = new StringBuilder();
1✔
67
            this.identStep = identStep;
1✔
68
        }
1✔
69

70
        public JsonStringBuilder() {
1✔
71
            builder = new StringBuilder();
1✔
72
            this.identStep = Step.TWO_SPACES;
1✔
73
        }
1✔
74

75
        public JsonStringBuilder append(final char character) {
76
            builder.append(character);
1✔
77
            return this;
1✔
78
        }
79

80
        public JsonStringBuilder append(final String string) {
81
            builder.append(string);
1✔
82
            return this;
1✔
83
        }
84

85
        public JsonStringBuilder fillSpaces() {
86
            builder.append(
1✔
87
                    String.valueOf(identStep == Step.TABS ? '\t' : ' ')
1✔
88
                            .repeat(Math.max(0, indent)));
1✔
89
            return this;
1✔
90
        }
91

92
        public JsonStringBuilder incIndent() {
93
            indent += identStep.getIndent();
1✔
94
            return this;
1✔
95
        }
96

97
        public JsonStringBuilder decIndent() {
98
            indent -= identStep.getIndent();
1✔
99
            return this;
1✔
100
        }
101

102
        public JsonStringBuilder newLine() {
103
            if (identStep != Step.COMPACT) {
1✔
104
                builder.append('\n');
1✔
105
            }
106
            return this;
1✔
107
        }
108

109
        public Step getIdentStep() {
110
            return identStep;
1✔
111
        }
112

113
        public String toString() {
114
            return builder.toString();
1✔
115
        }
116
    }
117

118
    public static class JsonArray {
119
        private JsonArray() {}
120

121
        public static void writeJson(Collection collection, JsonStringBuilder builder) {
122
            if (collection == null) {
1✔
123
                builder.append(NULL);
1✔
124
                return;
1✔
125
            }
126
            Iterator iter = collection.iterator();
1✔
127
            builder.append('[').incIndent();
1✔
128
            if (!collection.isEmpty()) {
1✔
129
                builder.newLine();
1✔
130
            }
131
            while (iter.hasNext()) {
1✔
132
                Object value = iter.next();
1✔
133
                builder.fillSpaces();
1✔
134
                JsonValue.writeJson(value, builder);
1✔
135
                if (iter.hasNext()) {
1✔
136
                    builder.append(',').newLine();
1✔
137
                }
138
            }
1✔
139
            builder.newLine().decIndent().fillSpaces().append(']');
1✔
140
        }
1✔
141

142
        public static void writeJson(byte[] byteArray, JsonStringBuilder builder) {
143
            if (byteArray == null) {
1✔
144
                builder.append(NULL);
1✔
145
            } else if (byteArray.length == 0) {
1✔
146
                builder.append("[]");
1✔
147
            } else {
148
                builder.append('[').incIndent().newLine();
1✔
149
                builder.fillSpaces().append(String.valueOf(byteArray[0]));
1✔
150
                for (int i = 1; i < byteArray.length; i++) {
1✔
151
                    builder.append(',').newLine().fillSpaces();
1✔
152
                    builder.append(String.valueOf(byteArray[i]));
1✔
153
                }
154
                builder.newLine().decIndent().fillSpaces().append(']');
1✔
155
            }
156
        }
1✔
157

158
        public static void writeJson(short[] shortArray, JsonStringBuilder builder) {
159
            if (shortArray == null) {
1✔
160
                builder.append(NULL);
1✔
161
            } else if (shortArray.length == 0) {
1✔
162
                builder.append("[]");
1✔
163
            } else {
164
                builder.append('[').incIndent().newLine();
1✔
165
                builder.fillSpaces().append(String.valueOf(shortArray[0]));
1✔
166
                for (int i = 1; i < shortArray.length; i++) {
1✔
167
                    builder.append(',').newLine().fillSpaces();
1✔
168
                    builder.append(String.valueOf(shortArray[i]));
1✔
169
                }
170
                builder.newLine().decIndent().fillSpaces().append(']');
1✔
171
            }
172
        }
1✔
173

174
        public static void writeJson(int[] intArray, JsonStringBuilder builder) {
175
            if (intArray == null) {
1✔
176
                builder.append(NULL);
1✔
177
            } else if (intArray.length == 0) {
1✔
178
                builder.append("[]");
1✔
179
            } else {
180
                builder.append('[').incIndent().newLine();
1✔
181
                builder.fillSpaces().append(String.valueOf(intArray[0]));
1✔
182
                for (int i = 1; i < intArray.length; i++) {
1✔
183
                    builder.append(',').newLine().fillSpaces();
1✔
184
                    builder.append(String.valueOf(intArray[i]));
1✔
185
                }
186
                builder.newLine().decIndent().fillSpaces().append(']');
1✔
187
            }
188
        }
1✔
189

190
        public static void writeJson(long[] longArray, JsonStringBuilder builder) {
191
            if (longArray == null) {
1✔
192
                builder.append(NULL);
1✔
193
            } else if (longArray.length == 0) {
1✔
194
                builder.append("[]");
1✔
195
            } else {
196
                builder.append('[').incIndent().newLine();
1✔
197
                builder.fillSpaces().append(String.valueOf(longArray[0]));
1✔
198
                for (int i = 1; i < longArray.length; i++) {
1✔
199
                    builder.append(',').newLine().fillSpaces();
1✔
200
                    builder.append(String.valueOf(longArray[i]));
1✔
201
                }
202
                builder.newLine().decIndent().fillSpaces().append(']');
1✔
203
            }
204
        }
1✔
205

206
        public static void writeJson(float[] floatArray, JsonStringBuilder builder) {
207
            if (floatArray == null) {
1✔
208
                builder.append(NULL);
1✔
209
            } else if (floatArray.length == 0) {
1✔
210
                builder.append("[]");
1✔
211
            } else {
212
                builder.append('[').incIndent().newLine();
1✔
213
                builder.fillSpaces().append(String.valueOf(floatArray[0]));
1✔
214
                for (int i = 1; i < floatArray.length; i++) {
1✔
215
                    builder.append(',').newLine().fillSpaces();
1✔
216
                    builder.append(String.valueOf(floatArray[i]));
1✔
217
                }
218
                builder.newLine().decIndent().fillSpaces().append(']');
1✔
219
            }
220
        }
1✔
221

222
        public static void writeJson(double[] doubleArray, JsonStringBuilder builder) {
223
            if (doubleArray == null) {
1✔
224
                builder.append(NULL);
1✔
225
            } else if (doubleArray.length == 0) {
1✔
226
                builder.append("[]");
1✔
227
            } else {
228
                builder.append('[').incIndent().newLine();
1✔
229
                builder.fillSpaces().append(String.valueOf(doubleArray[0]));
1✔
230
                for (int i = 1; i < doubleArray.length; i++) {
1✔
231
                    builder.append(',').newLine().fillSpaces();
1✔
232
                    builder.append(String.valueOf(doubleArray[i]));
1✔
233
                }
234
                builder.newLine().decIndent().fillSpaces().append(']');
1✔
235
            }
236
        }
1✔
237

238
        public static void writeJson(boolean[] booleanArray, JsonStringBuilder builder) {
239
            if (booleanArray == null) {
1✔
240
                builder.append(NULL);
1✔
241
            } else if (booleanArray.length == 0) {
1✔
242
                builder.append("[]");
1✔
243
            } else {
244
                builder.append('[').incIndent().newLine();
1✔
245
                builder.fillSpaces().append(String.valueOf(booleanArray[0]));
1✔
246
                for (int i = 1; i < booleanArray.length; i++) {
1✔
247
                    builder.append(',').newLine().fillSpaces();
1✔
248
                    builder.append(String.valueOf(booleanArray[i]));
1✔
249
                }
250
                builder.newLine().decIndent().fillSpaces().append(']');
1✔
251
            }
252
        }
1✔
253

254
        public static void writeJson(char[] charArray, JsonStringBuilder builder) {
255
            if (charArray == null) {
1✔
256
                builder.append(NULL);
1✔
257
            } else if (charArray.length == 0) {
1✔
258
                builder.append("[]");
1✔
259
            } else {
260
                builder.append('[').incIndent().newLine();
1✔
261
                builder.fillSpaces().append('\"').append(String.valueOf(charArray[0])).append('\"');
1✔
262
                for (int i = 1; i < charArray.length; i++) {
1✔
263
                    builder.append(',').newLine().fillSpaces();
1✔
264
                    builder.append('"').append(String.valueOf(charArray[i])).append('"');
1✔
265
                }
266
                builder.newLine().decIndent().fillSpaces().append(']');
1✔
267
            }
268
        }
1✔
269

270
        public static void writeJson(Object[] objectArray, JsonStringBuilder builder) {
271
            if (objectArray == null) {
1✔
272
                builder.append(NULL);
1✔
273
            } else if (objectArray.length == 0) {
1✔
274
                builder.append("[]");
1✔
275
            } else {
276
                builder.append('[').newLine().incIndent().fillSpaces();
1✔
277
                JsonValue.writeJson(objectArray[0], builder);
1✔
278
                for (int i = 1; i < objectArray.length; i++) {
1✔
279
                    builder.append(',').newLine().fillSpaces();
1✔
280
                    JsonValue.writeJson(objectArray[i], builder);
1✔
281
                }
282
                builder.newLine().decIndent().fillSpaces().append(']');
1✔
283
            }
284
        }
1✔
285
    }
286

287
    public static class JsonObject {
288
        private JsonObject() {}
289

290
        public static void writeJson(Map map, JsonStringBuilder builder) {
291
            if (map == null) {
1✔
292
                builder.append(NULL);
1✔
293
                return;
1✔
294
            }
295
            Iterator iter = map.entrySet().iterator();
1✔
296
            builder.append('{').incIndent();
1✔
297
            if (!map.isEmpty()) {
1✔
298
                builder.newLine();
1✔
299
            }
300
            while (iter.hasNext()) {
1✔
301
                Map.Entry entry = (Map.Entry) iter.next();
1✔
302
                builder.fillSpaces().append('"');
1✔
303
                builder.append(JsonValue.escape(String.valueOf(entry.getKey())));
1✔
304
                builder.append('"');
1✔
305
                builder.append(':');
1✔
306
                if (builder.getIdentStep() != JsonStringBuilder.Step.COMPACT) {
1✔
307
                    builder.append(' ');
1✔
308
                }
309
                JsonValue.writeJson(entry.getValue(), builder);
1✔
310
                if (iter.hasNext()) {
1✔
311
                    builder.append(',').newLine();
1✔
312
                }
313
            }
1✔
314
            builder.newLine().decIndent().fillSpaces().append('}');
1✔
315
        }
1✔
316
    }
317

318
    public static class JsonValue {
319
        private JsonValue() {}
320

321
        public static void writeJson(Object value, JsonStringBuilder builder) {
322
            if (value == null) {
1✔
323
                builder.append(NULL);
1✔
324
            } else if (value instanceof String) {
1✔
325
                builder.append('"').append(escape((String) value)).append('"');
1✔
326
            } else if (value instanceof Double) {
1✔
327
                if (((Double) value).isInfinite() || ((Double) value).isNaN()) {
1✔
328
                    builder.append(NULL);
1✔
329
                } else {
330
                    builder.append(value.toString());
1✔
331
                }
332
            } else if (value instanceof Float) {
1✔
333
                if (((Float) value).isInfinite() || ((Float) value).isNaN()) {
1✔
334
                    builder.append(NULL);
1✔
335
                } else {
336
                    builder.append(value.toString());
1✔
337
                }
338
            } else if (value instanceof Number) {
1✔
339
                builder.append(value.toString());
1✔
340
            } else if (value instanceof Boolean) {
1✔
341
                builder.append(value.toString());
1✔
342
            } else if (value instanceof Map) {
1✔
343
                JsonObject.writeJson((Map) value, builder);
1✔
344
            } else if (value instanceof Collection) {
1✔
345
                JsonArray.writeJson((Collection) value, builder);
1✔
346
            } else {
347
                doWriteJson(value, builder);
1✔
348
            }
349
        }
1✔
350

351
        private static void doWriteJson(Object value, JsonStringBuilder builder) {
352
            if (value instanceof byte[]) {
1✔
353
                JsonArray.writeJson((byte[]) value, builder);
1✔
354
            } else if (value instanceof short[]) {
1✔
355
                JsonArray.writeJson((short[]) value, builder);
1✔
356
            } else if (value instanceof int[]) {
1✔
357
                JsonArray.writeJson((int[]) value, builder);
1✔
358
            } else if (value instanceof long[]) {
1✔
359
                JsonArray.writeJson((long[]) value, builder);
1✔
360
            } else if (value instanceof float[]) {
1✔
361
                JsonArray.writeJson((float[]) value, builder);
1✔
362
            } else if (value instanceof double[]) {
1✔
363
                JsonArray.writeJson((double[]) value, builder);
1✔
364
            } else if (value instanceof boolean[]) {
1✔
365
                JsonArray.writeJson((boolean[]) value, builder);
1✔
366
            } else if (value instanceof char[]) {
1✔
367
                JsonArray.writeJson((char[]) value, builder);
1✔
368
            } else if (value instanceof Object[]) {
1✔
369
                JsonArray.writeJson((Object[]) value, builder);
1✔
370
            } else {
371
                builder.append('"').append(escape(value.toString())).append('"');
1✔
372
            }
373
        }
1✔
374

375
        public static String escape(String inputString) {
376
            if (inputString == null) {
1✔
377
                return null;
1✔
378
            }
379
            StringBuilder sb = new StringBuilder();
1✔
380
            escape(inputString, sb);
1✔
381
            return sb.toString();
1✔
382
        }
383

384
        private static void escape(String inputString, StringBuilder sb) {
385
            final int len = inputString.length();
1✔
386
            for (int i = 0; i < len; i++) {
1✔
387
                char ch = inputString.charAt(i);
1✔
388
                switch (ch) {
1✔
389
                    case '"':
390
                        sb.append("\\\"");
1✔
391
                        break;
1✔
392
                    case '\\':
393
                        sb.append("\\\\");
1✔
394
                        break;
1✔
395
                    case '\b':
396
                        sb.append("\\b");
1✔
397
                        break;
1✔
398
                    case '\f':
399
                        sb.append("\\f");
1✔
400
                        break;
1✔
401
                    case '\n':
402
                        sb.append("\\n");
1✔
403
                        break;
1✔
404
                    case '\r':
405
                        sb.append("\\r");
1✔
406
                        break;
1✔
407
                    case '\t':
408
                        sb.append("\\t");
1✔
409
                        break;
1✔
410
                    case '€':
411
                        sb.append('€');
1✔
412
                        break;
1✔
413
                    default:
414
                        if (ch <= '\u001F'
1✔
415
                                || ch >= '\u007F' && ch <= '\u009F'
416
                                || ch >= '\u2000' && ch <= '\u20FF') {
417
                            String ss = Integer.toHexString(ch);
1✔
418
                            sb.append("\\u");
1✔
419
                            sb.append("0".repeat(4 - ss.length()));
1✔
420
                            sb.append(ss.toUpperCase());
1✔
421
                        } else {
1✔
422
                            sb.append(ch);
1✔
423
                        }
424
                        break;
425
                }
426
            }
427
        }
1✔
428
    }
429

430
    public static class ParseException extends RuntimeException {
431
        private final int offset;
432
        private final int line;
433
        private final int column;
434

435
        public ParseException(String message, int offset, int line, int column) {
436
            super(String.format("%s at %d:%d", message, line, column));
1✔
437
            this.offset = offset;
1✔
438
            this.line = line;
1✔
439
            this.column = column;
1✔
440
        }
1✔
441

442
        public int getOffset() {
443
            return offset;
1✔
444
        }
445

446
        public int getLine() {
447
            return line;
1✔
448
        }
449

450
        public int getColumn() {
451
            return column;
1✔
452
        }
453
    }
454

455
    public static class JsonParser {
456
        private final String json;
457
        private int index;
458
        private int line;
459
        private int lineOffset;
460
        private int current;
461
        private final StringBuilder captureBuffer = new StringBuilder();
1✔
462
        private int captureStart;
463
        private final int maxDepth;
464

465
        public JsonParser(String string, int maxDepth) {
1✔
466
            this.json = string;
1✔
467
            this.maxDepth = maxDepth;
1✔
468
            line = 1;
1✔
469
            captureStart = -1;
1✔
470
        }
1✔
471

472
        public Object parse() {
473
            read();
1✔
474
            skipWhiteSpace();
1✔
475
            final Object result = readValue(0);
1✔
476
            skipWhiteSpace();
1✔
477
            if (!isEndOfText()) {
1✔
478
                throw error("Unexpected character");
1✔
479
            }
480
            return result;
1✔
481
        }
482

483
        private Object readValue(int depth) {
484
            if (depth > maxDepth) {
1✔
485
                throw error("Maximum depth exceeded");
1✔
486
            }
487
            switch (current) {
1✔
488
                case 'n':
489
                    return readNull();
1✔
490
                case 't':
491
                    return readTrue();
1✔
492
                case 'f':
493
                    return readFalse();
1✔
494
                case '"':
495
                    return readString();
1✔
496
                case '[':
497
                    return readArray(depth + 1);
1✔
498
                case '{':
499
                    return readObject(depth + 1);
1✔
500
                case '-':
501
                case '0':
502
                case '1':
503
                case '2':
504
                case '3':
505
                case '4':
506
                case '5':
507
                case '6':
508
                case '7':
509
                case '8':
510
                case '9':
511
                    return readNumber();
1✔
512
                default:
513
                    throw expected("value");
1✔
514
            }
515
        }
516

517
        private List<Object> readArray(int depth) {
518
            read();
1✔
519
            List<Object> array = new ArrayList<>();
1✔
520
            skipWhiteSpace();
1✔
521
            if (readChar(']')) {
1✔
522
                return array;
1✔
523
            }
524
            do {
525
                skipWhiteSpace();
1✔
526
                array.add(readValue(depth));
1✔
527
                skipWhiteSpace();
1✔
528
            } while (readChar(','));
1✔
529
            if (!readChar(']')) {
1✔
530
                throw expected("',' or ']'");
1✔
531
            }
532
            return array;
1✔
533
        }
534

535
        private Map<String, Object> readObject(int depth) {
536
            read();
1✔
537
            Map<String, Object> object = new LinkedHashMap<>();
1✔
538
            skipWhiteSpace();
1✔
539
            if (readChar('}')) {
1✔
540
                return object;
1✔
541
            }
542
            do {
543
                skipWhiteSpace();
1✔
544
                String name = readName();
1✔
545
                skipWhiteSpace();
1✔
546
                if (!readChar(':')) {
1✔
547
                    throw expected("':'");
1✔
548
                }
549
                skipWhiteSpace();
1✔
550
                object.put(name, readValue(depth));
1✔
551
                skipWhiteSpace();
1✔
552
            } while (readChar(','));
1✔
553
            if (!readChar('}')) {
1✔
554
                throw expected("',' or '}'");
1✔
555
            }
556
            return object;
1✔
557
        }
558

559
        private String readName() {
560
            if (current != '"') {
1✔
561
                throw expected("name");
1✔
562
            }
563
            return readString();
1✔
564
        }
565

566
        private String readNull() {
567
            read();
1✔
568
            readRequiredChar('u');
1✔
569
            readRequiredChar('l');
1✔
570
            readRequiredChar('l');
1✔
571
            return null;
1✔
572
        }
573

574
        private Boolean readTrue() {
575
            read();
1✔
576
            readRequiredChar('r');
1✔
577
            readRequiredChar('u');
1✔
578
            readRequiredChar('e');
1✔
579
            return Boolean.TRUE;
1✔
580
        }
581

582
        private Boolean readFalse() {
583
            read();
1✔
584
            readRequiredChar('a');
1✔
585
            readRequiredChar('l');
1✔
586
            readRequiredChar('s');
1✔
587
            readRequiredChar('e');
1✔
588
            return Boolean.FALSE;
1✔
589
        }
590

591
        private void readRequiredChar(char ch) {
592
            if (!readChar(ch)) {
1✔
593
                throw expected("'" + ch + "'");
1✔
594
            }
595
        }
1✔
596

597
        private String readString() {
598
            read();
1✔
599
            startCapture();
1✔
600
            while (current != '"') {
1✔
601
                if (current == '\\') {
1✔
602
                    pauseCapture();
1✔
603
                    readEscape();
1✔
604
                    startCapture();
1✔
605
                } else if (current < 0x20) {
1✔
606
                    throw expected("valid string character");
1✔
607
                } else {
608
                    read();
1✔
609
                }
610
            }
611
            String string = endCapture();
1✔
612
            read();
1✔
613
            return string;
1✔
614
        }
615

616
        private void readEscape() {
617
            read();
1✔
618
            switch (current) {
1✔
619
                case '"':
620
                case '/':
621
                case '\\':
622
                    captureBuffer.append((char) current);
1✔
623
                    break;
1✔
624
                case 'b':
625
                    captureBuffer.append('\b');
1✔
626
                    break;
1✔
627
                case 'f':
628
                    captureBuffer.append('\f');
1✔
629
                    break;
1✔
630
                case 'n':
631
                    captureBuffer.append('\n');
1✔
632
                    break;
1✔
633
                case 'r':
634
                    captureBuffer.append('\r');
1✔
635
                    break;
1✔
636
                case 't':
637
                    captureBuffer.append('\t');
1✔
638
                    break;
1✔
639
                case 'u':
640
                    char[] hexChars = new char[4];
1✔
641
                    boolean isHexCharsDigits = true;
1✔
642
                    for (int i = 0; i < 4; i++) {
1✔
643
                        read();
1✔
644
                        if (!isHexDigit()) {
1✔
645
                            isHexCharsDigits = false;
1✔
646
                        }
647
                        hexChars[i] = (char) current;
1✔
648
                    }
649
                    if (isHexCharsDigits) {
1✔
650
                        captureBuffer.append((char) Integer.parseInt(new String(hexChars), 16));
1✔
651
                    } else {
652
                        captureBuffer
1✔
653
                                .append("\\u")
1✔
654
                                .append(hexChars[0])
1✔
655
                                .append(hexChars[1])
1✔
656
                                .append(hexChars[2])
1✔
657
                                .append(hexChars[3]);
1✔
658
                    }
659
                    break;
1✔
660
                default:
661
                    throw expected("valid escape sequence");
1✔
662
            }
663
            read();
1✔
664
        }
1✔
665

666
        private Number readNumber() {
667
            startCapture();
1✔
668
            readChar('-');
1✔
669
            int firstDigit = current;
1✔
670
            if (!readDigit()) {
1✔
671
                throw expected(DIGIT);
1✔
672
            }
673
            if (firstDigit != '0') {
1✔
674
                while (readDigit()) {
1✔
675
                    // ignored
676
                }
677
            }
678
            readFraction();
1✔
679
            readExponent();
1✔
680
            final String number = endCapture();
1✔
681
            final Number result;
682
            if (number.contains(".") || number.contains("e") || number.contains("E")) {
1✔
683
                if (number.length() > 9
1✔
684
                        || (number.contains(".") && number.length() - number.lastIndexOf('.') > 2)
1✔
685
                                && number.charAt(number.length() - 1) == '0') {
1✔
686
                    result = new java.math.BigDecimal(number);
1✔
687
                } else {
688
                    result = Double.valueOf(number);
1✔
689
                }
690
            } else {
691
                if (number.length() > 19) {
1✔
692
                    result = new java.math.BigInteger(number);
1✔
693
                } else {
694
                    result = Long.valueOf(number);
1✔
695
                }
696
            }
697
            return result;
1✔
698
        }
699

700
        private void readFraction() {
701
            if (!readChar('.')) {
1✔
702
                return;
1✔
703
            }
704
            if (!readDigit()) {
1✔
705
                throw expected(DIGIT);
1✔
706
            }
707
            while (readDigit()) {
1✔
708
                // ignored
709
            }
710
        }
1✔
711

712
        private void readExponent() {
713
            if (!readChar('e') && !readChar('E')) {
1✔
714
                return;
1✔
715
            }
716
            if (!readChar('+')) {
1✔
717
                readChar('-');
1✔
718
            }
719
            if (!readDigit()) {
1✔
720
                throw expected(DIGIT);
1✔
721
            }
722
            while (readDigit()) {
1✔
723
                // ignored
724
            }
725
        }
1✔
726

727
        private boolean readChar(char ch) {
728
            if (current != ch) {
1✔
729
                return false;
1✔
730
            }
731
            read();
1✔
732
            return true;
1✔
733
        }
734

735
        private boolean readDigit() {
736
            if (!isDigit()) {
1✔
737
                return false;
1✔
738
            }
739
            read();
1✔
740
            return true;
1✔
741
        }
742

743
        private void skipWhiteSpace() {
744
            while (isWhiteSpace()) {
1✔
745
                read();
1✔
746
            }
747
        }
1✔
748

749
        private void read() {
750
            if (index == json.length()) {
1✔
751
                current = -1;
1✔
752
                return;
1✔
753
            }
754
            if (current == '\n') {
1✔
755
                line++;
1✔
756
                lineOffset = index;
1✔
757
            }
758
            current = json.charAt(index++);
1✔
759
        }
1✔
760

761
        private void startCapture() {
762
            captureStart = index - 1;
1✔
763
        }
1✔
764

765
        private void pauseCapture() {
766
            captureBuffer.append(json, captureStart, index - 1);
1✔
767
            captureStart = -1;
1✔
768
        }
1✔
769

770
        private String endCapture() {
771
            int end = current == -1 ? index : index - 1;
1✔
772
            String captured;
773
            if (captureBuffer.length() > 0) {
1✔
774
                captureBuffer.append(json, captureStart, end);
1✔
775
                captured = captureBuffer.toString();
1✔
776
                captureBuffer.setLength(0);
1✔
777
            } else {
778
                captured = json.substring(captureStart, end);
1✔
779
            }
780
            captureStart = -1;
1✔
781
            return captured;
1✔
782
        }
783

784
        private ParseException expected(String expected) {
785
            if (isEndOfText()) {
1✔
786
                return error("Unexpected end of input");
1✔
787
            }
788
            return error("Expected " + expected);
1✔
789
        }
790

791
        private ParseException error(String message) {
792
            int absIndex = index;
1✔
793
            int column = absIndex - lineOffset;
1✔
794
            int offset = isEndOfText() ? absIndex : absIndex - 1;
1✔
795
            return new ParseException(message, offset, line, column - 1);
1✔
796
        }
797

798
        private boolean isWhiteSpace() {
799
            return current == ' ' || current == '\t' || current == '\n' || current == '\r';
1✔
800
        }
801

802
        private boolean isDigit() {
803
            return current >= '0' && current <= '9';
1✔
804
        }
805

806
        private boolean isHexDigit() {
807
            return isDigit()
1✔
808
                    || current >= 'a' && current <= 'f'
809
                    || current >= 'A' && current <= 'F';
810
        }
811

812
        private boolean isEndOfText() {
813
            return current == -1;
1✔
814
        }
815
    }
816

817
    public static String toJson(Collection collection, JsonStringBuilder.Step identStep) {
818
        final JsonStringBuilder builder = new JsonStringBuilder(identStep);
1✔
819
        JsonArray.writeJson(collection, builder);
1✔
820
        return builder.toString();
1✔
821
    }
822

823
    public static String toJson(Collection collection) {
824
        return toJson(collection, JsonStringBuilder.Step.TWO_SPACES);
1✔
825
    }
826

827
    public static String toJson(Map map, JsonStringBuilder.Step identStep) {
828
        final JsonStringBuilder builder = new JsonStringBuilder(identStep);
1✔
829
        JsonObject.writeJson(map, builder);
1✔
830
        return builder.toString();
1✔
831
    }
832

833
    public static String toJson(Map map) {
834
        return toJson(map, JsonStringBuilder.Step.TWO_SPACES);
1✔
835
    }
836

837
    public static Object fromJson(String string) {
838
        return fromJson(string, PARSE_MAX_DEPTH);
1✔
839
    }
840

841
    public static Object fromJson(String string, int maxDepth) {
842
        return new JsonParser(string, maxDepth).parse();
1✔
843
    }
844

845
    public static String formatJson(String json, JsonStringBuilder.Step identStep) {
846
        Object result = fromJson(json);
1✔
847
        if (result instanceof Map) {
1✔
848
            return toJson((Map) result, identStep);
1✔
849
        }
850
        return toJson((List) result, identStep);
1✔
851
    }
852

853
    public static String formatJson(String json) {
854
        return formatJson(json, JsonStringBuilder.Step.TWO_SPACES);
1✔
855
    }
856
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc