1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.io.core.internal;
18
19 import java.io.Reader;
20 import java.io.StringReader;
21 import java.util.function.IntPredicate;
22
23 import org.apache.commons.geometry.core.GeometryTestUtils;
24 import org.junit.jupiter.api.Assertions;
25 import org.junit.jupiter.api.Test;
26
27 class SimpleTextParserTest {
28
29 private static final double EPS = 1e-20;
30
31 private static final int EOF = -1;
32
33 @Test
34 void testMaxStringLength_defaultValue() {
35
36 final SimpleTextParser p = parser("abc");
37
38
39 Assertions.assertEquals(1024, p.getMaxStringLength());
40 }
41
42 @Test
43 void testMaxStringLength_illegalArg() {
44
45 final SimpleTextParser p = parser("abc");
46
47
48 GeometryTestUtils.assertThrowsWithMessage(() -> {
49 p.setMaxStringLength(-1);
50 }, IllegalArgumentException.class, "Maximum string length cannot be less than zero; was -1");
51 }
52
53 @Test
54 void testCharacterSequence() {
55
56 assertCharacterSequence(parser(""), "");
57 assertCharacterSequence(parser("abc def"), "abc def");
58 }
59
60 @Test
61 void testCharacterPosition() {
62
63 final SimpleTextParser p = parser(
64 "a b\n" +
65 "\r\n" +
66 "d \r" +
67 "e");
68
69
70 assertPosition(p, 1, 1);
71 assertChar('a', p.readChar());
72
73 assertPosition(p, 1, 2);
74 assertChar(' ', p.readChar());
75
76 assertPosition(p, 1, 3);
77 assertChar('b', p.readChar());
78
79 assertPosition(p, 1, 4);
80 assertChar('\n', p.readChar());
81
82 assertPosition(p, 2, 1);
83 assertChar('\r', p.readChar());
84
85 assertPosition(p, 2, 2);
86 assertChar('\n', p.readChar());
87
88 assertPosition(p, 3, 1);
89 assertChar('d', p.readChar());
90
91 assertPosition(p, 3, 2);
92 assertChar(' ', p.readChar());
93
94 assertPosition(p, 3, 3);
95 assertChar('\r', p.readChar());
96
97 assertPosition(p, 4, 1);
98 assertChar('e', p.readChar());
99
100 assertPosition(p, 4, 2);
101 assertChar(EOF, p.readChar());
102 }
103
104 @Test
105 void testCharacterPosition_givenPosition() {
106
107 final SimpleTextParser p = parser("abc\rdef");
108
109
110 assertPosition(p, 1, 1);
111
112 p.setLineNumber(10);
113 p.setColumnNumber(3);
114
115 assertPosition(p, 10, 3);
116
117 p.discard(4);
118
119 assertPosition(p, 11, 1);
120
121 p.discard(3);
122
123 assertPosition(p, 11, 4);
124 }
125
126 @Test
127 void testHasMoreCharacters() {
128
129 final SimpleTextParser empty = parser("");
130 final SimpleTextParser nonEmpty = parser("a");
131
132
133 Assertions.assertFalse(empty.hasMoreCharacters());
134
135 Assertions.assertTrue(nonEmpty.hasMoreCharacters());
136 assertChar('a', nonEmpty.readChar());
137 Assertions.assertFalse(nonEmpty.hasMoreCharacters());
138 }
139
140 @Test
141 void testHasMoreCharactersOnLine() {
142
143 final SimpleTextParser empty = parser("");
144 final SimpleTextParser singleLine = parser("a");
145 final SimpleTextParser multiLine = parser("a\r\nb\rc\n\n");
146
147
148 Assertions.assertFalse(empty.hasMoreCharactersOnLine());
149
150 Assertions.assertTrue(singleLine.hasMoreCharactersOnLine());
151 assertChar('a', singleLine.readChar());
152 Assertions.assertFalse(singleLine.hasMoreCharactersOnLine());
153
154 Assertions.assertTrue(multiLine.hasMoreCharactersOnLine());
155 assertChar('a', multiLine.readChar());
156
157 Assertions.assertFalse(multiLine.hasMoreCharactersOnLine());
158 assertChar('\r', multiLine.readChar());
159
160 Assertions.assertFalse(multiLine.hasMoreCharactersOnLine());
161 assertChar('\n', multiLine.readChar());
162
163 Assertions.assertTrue(multiLine.hasMoreCharactersOnLine());
164 assertChar('b', multiLine.readChar());
165
166 Assertions.assertFalse(multiLine.hasMoreCharactersOnLine());
167 assertChar('\r', multiLine.readChar());
168
169 Assertions.assertTrue(multiLine.hasMoreCharactersOnLine());
170 assertChar('c', multiLine.readChar());
171
172 Assertions.assertFalse(multiLine.hasMoreCharactersOnLine());
173 assertChar('\n', multiLine.readChar());
174
175 Assertions.assertFalse(multiLine.hasMoreCharactersOnLine());
176 assertChar('\n', multiLine.readChar());
177
178 Assertions.assertFalse(multiLine.hasMoreCharactersOnLine());
179 assertChar(EOF, multiLine.readChar());
180 }
181
182 @Test
183 void testBasicTokenMethods() {
184
185 final SimpleTextParser p = parser("abcdef\r\n\r ghi");
186
187
188 assertToken(p, null, -1, -1);
189 Assertions.assertFalse(p.hasNonEmptyToken());
190
191 assertToken(p.next(1), "a", 1, 1);
192 Assertions.assertTrue(p.hasNonEmptyToken());
193
194 assertToken(p.next(3), "bcd", 1, 2);
195 Assertions.assertTrue(p.hasNonEmptyToken());
196
197 assertToken(p.next(5), "ef\r\n\r", 1, 5);
198 Assertions.assertTrue(p.hasNonEmptyToken());
199
200 assertToken(p.next(0), "", 3, 1);
201 Assertions.assertFalse(p.hasNonEmptyToken());
202
203 assertToken(p.next(1), " ", 3, 1);
204 Assertions.assertTrue(p.hasNonEmptyToken());
205
206 assertToken(p.next(3), "ghi", 3, 2);
207 Assertions.assertTrue(p.hasNonEmptyToken());
208
209 assertToken(p.next(1), null, 3, 5);
210 Assertions.assertFalse(p.hasNonEmptyToken());
211 }
212
213 @Test
214 void testGetCurrentTokenAsDouble() {
215
216 final SimpleTextParser p = parser("1e-4\n+5\n-4.001");
217
218
219 p.nextLine();
220 Assertions.assertEquals(1e-4, p.getCurrentTokenAsDouble(), EPS);
221
222 p.nextLine();
223 Assertions.assertEquals(5.0, p.getCurrentTokenAsDouble(), EPS);
224
225 p.nextLine();
226 Assertions.assertEquals(-4.001, p.getCurrentTokenAsDouble(), EPS);
227 }
228
229 @Test
230 void testGetCurrentTokenAsDouble_failures() {
231
232 final SimpleTextParser p = parser("abc\n1.1.1a");
233
234
235 GeometryTestUtils.assertThrowsWithMessage(() -> {
236 p.getCurrentTokenAsDouble();
237 }, IllegalStateException.class, "No token has been read from the character stream");
238
239 p.next(SimpleTextParser::isNotNewLinePart);
240 GeometryTestUtils.assertThrowsWithMessage(() -> {
241 p.getCurrentTokenAsDouble();
242 }, IllegalStateException.class,
243 "Parsing failed at line 1, column 1: expected double but found [abc]");
244
245 p.nextAlphanumeric();
246 GeometryTestUtils.assertThrowsWithMessage(() -> {
247 p.getCurrentTokenAsDouble();
248 }, IllegalStateException.class,
249 "Parsing failed at line 1, column 4: expected double but found end of line");
250
251 p.discardLine()
252 .next(c -> c != 'a');
253 GeometryTestUtils.assertThrowsWithMessage(() -> {
254 p.getCurrentTokenAsDouble();
255 }, IllegalStateException.class,
256 "Parsing failed at line 2, column 1: expected double but found [1.1.1]");
257
258 p.next(Character::isDigit);
259 GeometryTestUtils.assertThrowsWithMessage(() -> {
260 p.getCurrentTokenAsDouble();
261 }, IllegalStateException.class,
262 "Parsing failed at line 2, column 6: expected double but found empty token followed by [a]");
263
264 p.nextLine();
265 GeometryTestUtils.assertThrowsWithMessage(() -> {
266 p.getCurrentTokenAsDouble();
267 }, IllegalStateException.class,
268 "Parsing failed at line 2, column 6: expected double but found [a]");
269
270 p.nextLine();
271 GeometryTestUtils.assertThrowsWithMessage(() -> {
272 p.getCurrentTokenAsDouble();
273 }, IllegalStateException.class,
274 "Parsing failed at line 2, column 7: expected double but found end of content");
275 }
276
277 @Test
278 void testGetCurrentTokenAsDouble_includedNumberFormatExceptionOnFailure() {
279
280 final SimpleTextParser p = parser("abc");
281 p.nextLine();
282
283
284 final Throwable exc = Assertions.assertThrows(IllegalStateException.class, () -> p.getCurrentTokenAsDouble());
285 Assertions.assertEquals(NumberFormatException.class, exc.getCause().getClass());
286 }
287
288 @Test
289 void testGetCurrentTokenAsInt() {
290
291 final SimpleTextParser p = parser("0\n+5\n-401");
292
293
294 p.nextLine();
295 Assertions.assertEquals(0, p.getCurrentTokenAsInt());
296
297 p.nextLine();
298 Assertions.assertEquals(5, p.getCurrentTokenAsInt());
299
300 p.nextLine();
301 Assertions.assertEquals(-401, p.getCurrentTokenAsInt());
302 }
303
304 @Test
305 void testGetCurrentTokenAsInt_failures() {
306
307 final SimpleTextParser p = parser("abc\n1.1.1a");
308
309
310 GeometryTestUtils.assertThrowsWithMessage(() -> {
311 p.getCurrentTokenAsInt();
312 }, IllegalStateException.class, "No token has been read from the character stream");
313
314 p.next(SimpleTextParser::isNotNewLinePart);
315 GeometryTestUtils.assertThrowsWithMessage(() -> {
316 p.getCurrentTokenAsInt();
317 }, IllegalStateException.class,
318 "Parsing failed at line 1, column 1: expected integer but found [abc]");
319
320 p.nextAlphanumeric();
321 GeometryTestUtils.assertThrowsWithMessage(() -> {
322 p.getCurrentTokenAsInt();
323 }, IllegalStateException.class,
324 "Parsing failed at line 1, column 4: expected integer but found end of line");
325
326 p.discardLine()
327 .next(c -> c != 'a');
328 GeometryTestUtils.assertThrowsWithMessage(() -> {
329 p.getCurrentTokenAsInt();
330 }, IllegalStateException.class,
331 "Parsing failed at line 2, column 1: expected integer but found [1.1.1]");
332
333 p.next(Character::isDigit);
334 GeometryTestUtils.assertThrowsWithMessage(() -> {
335 p.getCurrentTokenAsInt();
336 }, IllegalStateException.class,
337 "Parsing failed at line 2, column 6: expected integer but found empty token followed by [a]");
338
339 p.nextLine();
340 GeometryTestUtils.assertThrowsWithMessage(() -> {
341 p.getCurrentTokenAsInt();
342 }, IllegalStateException.class,
343 "Parsing failed at line 2, column 6: expected integer but found [a]");
344
345 p.nextLine();
346 GeometryTestUtils.assertThrowsWithMessage(() -> {
347 p.getCurrentTokenAsInt();
348 }, IllegalStateException.class,
349 "Parsing failed at line 2, column 7: expected integer but found end of content");
350 }
351
352 @Test
353 void testGetCurrentTokenAsInt_includedNumberFormatExceptionOnFailure() {
354
355 final SimpleTextParser p = parser("abc");
356 p.nextLine();
357
358
359 final Throwable exc = Assertions.assertThrows(IllegalStateException.class, () -> p.getCurrentTokenAsInt());
360 Assertions.assertEquals(NumberFormatException.class, exc.getCause().getClass());
361 }
362
363 @Test
364 void testNext_lenArg() {
365
366 final SimpleTextParser p = parser("abcdef\r\n\r ghi");
367
368
369 assertToken(p.next(0), "", 1, 1);
370 assertToken(p.next(4), "abcd", 1, 1);
371 assertToken(p.next(6), "ef\r\n\r ", 1, 5);
372 assertToken(p.next(100), "ghi", 3, 2);
373
374 assertToken(p.next(0), null, 3, 5);
375 assertToken(p.next(100), null, 3, 5);
376 }
377
378 @Test
379 void testNextWithLineContinuation_lenArg() {
380
381 final char cont = '\\';
382 final SimpleTextParser p = parser("a\\bcdef\\\r\n\r ghi\\\n\\\n\\\rj");
383
384
385 assertToken(p.nextWithLineContinuation(cont, 0), "", 1, 1);
386 assertToken(p.nextWithLineContinuation(cont, 5), "a\\bcd", 1, 1);
387 assertToken(p.nextWithLineContinuation(cont, 3), "ef\r", 1, 6);
388 assertToken(p.nextWithLineContinuation(cont, 100), " ghij", 3, 1);
389
390 assertToken(p.nextWithLineContinuation(cont, 0), null, 6, 2);
391 assertToken(p.nextWithLineContinuation(cont, 100), null, 6, 2);
392 }
393
394 @Test
395 void testNext_lenArg_invalidArg() {
396
397 final SimpleTextParser p = parser("abc");
398 p.setMaxStringLength(2);
399
400
401 GeometryTestUtils.assertThrowsWithMessage(() -> {
402 p.next(-1);
403 }, IllegalArgumentException.class, "Requested string length cannot be negative; was -1");
404
405 GeometryTestUtils.assertThrowsWithMessage(() -> {
406 p.next(3);
407 }, IllegalArgumentException.class, "Requested string length of 3 exceeds maximum value of 2");
408 }
409
410 @Test
411 void testNext_predicateArg() {
412
413 final SimpleTextParser p = parser("a\n 012\r\ndef");
414
415
416 assertToken(p.next(c -> false), "", 1, 1);
417
418 assertToken(p.next(Character::isAlphabetic), "a", 1, 1);
419 assertToken(p.next(Character::isAlphabetic), "", 1, 2);
420
421 assertToken(p.next(Character::isWhitespace), "\n ", 1, 2);
422 assertToken(p.next(Character::isWhitespace), "", 2, 2);
423
424 assertToken(p.next(Character::isDigit), "012", 2, 2);
425 assertToken(p.next(Character::isDigit), "", 2, 5);
426
427 assertToken(p.next(Character::isWhitespace), "\r\n", 2, 5);
428 assertToken(p.next(Character::isWhitespace), "", 3, 1);
429
430 assertToken(p.next(c -> true), "def", 3, 1);
431 assertToken(p.next(c -> true), null, 3, 4);
432 }
433
434 @Test
435 void testNext_predicateArg_exceedsMaxStringLength() {
436
437 final SimpleTextParser p = parser("abcdef");
438 p.setMaxStringLength(4);
439
440
441 GeometryTestUtils.assertThrowsWithMessage(() -> {
442 p.next(c -> !Character.isWhitespace(c));
443 }, IllegalStateException.class, "Parsing failed at line 1, column 1: string length exceeds maximum value of 4");
444 }
445
446 @Test
447 void testNextWithLineContinuation_predicateArg() {
448
449 final char cont = '|';
450 final SimpleTextParser p = parser("|\na\n 0|\r\n|\r12\r\nd|ef");
451
452
453 assertToken(p.nextWithLineContinuation(cont, c -> false), "", 1, 1);
454
455 assertToken(p.nextWithLineContinuation(cont, Character::isAlphabetic), "a", 2, 1);
456 assertToken(p.nextWithLineContinuation(cont, Character::isAlphabetic), "", 2, 2);
457
458 assertToken(p.nextWithLineContinuation(cont, Character::isWhitespace), "\n ", 2, 2);
459 assertToken(p.nextWithLineContinuation(cont, Character::isWhitespace), "", 3, 2);
460
461 assertToken(p.nextWithLineContinuation(cont, Character::isDigit), "012", 3, 2);
462 assertToken(p.nextWithLineContinuation(cont, Character::isDigit), "", 5, 3);
463
464 assertToken(p.nextWithLineContinuation(cont, Character::isWhitespace), "\r\n", 5, 3);
465 assertToken(p.nextWithLineContinuation(cont, Character::isWhitespace), "", 6, 1);
466
467 assertToken(p.nextWithLineContinuation(cont, c -> true), "d|ef", 6, 1);
468 assertToken(p.nextWithLineContinuation(cont, c -> true), null, 6, 5);
469 }
470
471 @Test
472 void testNextLine() {
473
474 final SimpleTextParser p = parser("a\n 012\r\ndef\n\nx");
475
476
477 assertToken(p.nextLine(), "a", 1, 1);
478
479 assertToken(p.nextLine(), " 012", 2, 1);
480
481 p.readChar();
482 assertToken(p.nextLine(), "ef", 3, 2);
483
484 assertToken(p.nextLine(), "", 4, 1);
485
486 assertToken(p.nextLine(), "x", 5, 1);
487 assertToken(p.nextLine(), null, 5, 2);
488 }
489
490 @Test
491 void testNextAlphanumeric() {
492
493 final SimpleTextParser p = parser("a10Fd;X23456789-0\ny");
494
495
496 assertToken(p.nextAlphanumeric(), "a10Fd", 1, 1);
497
498 assertChar(';', p.readChar());
499 assertToken(p.nextAlphanumeric(), "X23456789", 1, 7);
500
501 assertChar('-', p.readChar());
502 assertToken(p.nextAlphanumeric(), "0", 1, 17);
503
504 assertToken(p.nextAlphanumeric(), "", 1, 18);
505
506 assertChar('\n', p.readChar());
507 assertToken(p.nextAlphanumeric(), "y", 2, 1);
508
509 assertToken(p.nextAlphanumeric(), null, 2, 2);
510 }
511
512 @Test
513 void testDiscard_lenArg() {
514
515 final SimpleTextParser p = parser("\na,b c\r\n12.3\rdef\n");
516
517
518 p.discard(0);
519 assertChar('\n', p.peekChar());
520 assertPosition(p, 1, 1);
521
522 p.discard(1);
523 assertChar('a', p.peekChar());
524 assertPosition(p, 2, 1);
525
526 p.discard(8);
527 assertChar('2', p.peekChar());
528 assertPosition(p, 3, 2);
529
530 p.discard(100);
531 assertChar(EOF, p.peekChar());
532 assertPosition(p, 5, 1);
533
534 p.discard(0);
535 assertChar(EOF, p.peekChar());
536 assertPosition(p, 5, 1);
537
538 p.discard(100);
539 assertChar(EOF, p.peekChar());
540 assertPosition(p, 5, 1);
541 }
542
543 @Test
544 void testDiscardWithLineContinuation_lenArg() {
545
546 final char cont = '|';
547 final SimpleTextParser p = parser("\n|a|\r\n,b|\n|\r c\r\n12.3\rdef\n");
548
549
550 p.discardWithLineContinuation(cont, 0);
551 assertChar('\n', p.peekChar());
552 assertPosition(p, 1, 1);
553
554 p.discardWithLineContinuation(cont, 1);
555 assertChar('|', p.peekChar());
556 assertPosition(p, 2, 1);
557
558 p.discardWithLineContinuation(cont, 8);
559 assertChar('1', p.peekChar());
560 assertPosition(p, 6, 1);
561
562 p.discardWithLineContinuation(cont, 100);
563 assertChar(EOF, p.peekChar());
564 assertPosition(p, 8, 1);
565
566 p.discardWithLineContinuation(cont, 0);
567 assertChar(EOF, p.peekChar());
568 assertPosition(p, 8, 1);
569
570 p.discardWithLineContinuation(cont, 100);
571 assertChar(EOF, p.peekChar());
572 assertPosition(p, 8, 1);
573 }
574
575 @Test
576 void testDiscard_predicateArg() {
577
578 final SimpleTextParser p = parser("\na,b c\r\n12.3\rdef\n");
579
580
581 p.discard(c -> Character.isWhitespace(c));
582 assertChar('a', p.peekChar());
583 assertPosition(p, 2, 1);
584
585 p.discard(c -> !Character.isWhitespace(c));
586 assertChar(' ', p.peekChar());
587 assertPosition(p, 2, 4);
588
589 p.discard(c -> Character.isDigit(c));
590 assertChar(' ', p.peekChar());
591 assertPosition(p, 2, 4);
592
593 p.discard(c -> Character.isWhitespace(c));
594 assertChar('c', p.peekChar());
595 assertPosition(p, 2, 5);
596
597 p.discard(c -> c != 'd');
598 assertChar('d', p.peekChar());
599 assertPosition(p, 4, 1);
600
601 p.discard(c -> true);
602 assertChar(EOF, p.peekChar());
603 assertPosition(p, 5, 1);
604
605 p.discard(c -> true);
606 assertChar(EOF, p.peekChar());
607 assertPosition(p, 5, 1);
608 }
609
610 @Test
611 void testDiscardWithLineContinuation_predicateArg() {
612
613 final char cont = '|';
614 final SimpleTextParser p = parser("\na,|\r\nb |c\r\n1|\r|\n2.3\rdef\n");
615
616
617 p.discardWithLineContinuation(cont, c -> Character.isWhitespace(c));
618 assertChar('a', p.peekChar());
619 assertPosition(p, 2, 1);
620
621 p.discardWithLineContinuation(cont, c -> !Character.isWhitespace(c));
622 assertChar(' ', p.peekChar());
623 assertPosition(p, 3, 2);
624
625 p.discardWithLineContinuation(cont, c -> Character.isDigit(c));
626 assertChar(' ', p.peekChar());
627 assertPosition(p, 3, 2);
628
629 p.discardWithLineContinuation(cont, c -> Character.isWhitespace(c));
630 assertChar('|', p.peekChar());
631 assertPosition(p, 3, 3);
632
633 p.discardWithLineContinuation(cont, c -> c != 'd');
634 assertChar('d', p.peekChar());
635 assertPosition(p, 7, 1);
636
637 p.discardWithLineContinuation(cont, c -> true);
638 assertChar(EOF, p.peekChar());
639 assertPosition(p, 8, 1);
640
641 p.discardWithLineContinuation(cont, c -> true);
642 assertChar(EOF, p.peekChar());
643 assertPosition(p, 8, 1);
644 }
645
646 @Test
647 void testDiscardWhitespace() {
648
649 final SimpleTextParser p = parser("a\t\n\r\n b c");
650
651
652 p.discardWhitespace();
653 assertPosition(p, 1, 1);
654 assertChar('a', p.readChar());
655
656 p.discardWhitespace();
657 assertPosition(p, 3, 4);
658 assertChar('b', p.readChar());
659
660 p.discardWhitespace();
661 assertPosition(p, 3, 6);
662 assertChar('c', p.readChar());
663
664 p.discardWhitespace();
665 assertPosition(p, 3, 7);
666 assertChar(EOF, p.readChar());
667 }
668
669 @Test
670 void testDiscardLineWhitespace() {
671
672 final SimpleTextParser p = parser("a\t\n\r\n b c");
673
674
675 p.discardLineWhitespace();
676 assertPosition(p, 1, 1);
677 assertChar('a', p.readChar());
678
679 p.discardLineWhitespace();
680 assertPosition(p, 1, 3);
681 assertChar('\n', p.peekChar());
682
683 p.discardLineWhitespace();
684 assertPosition(p, 1, 3);
685 assertChar('\n', p.readChar());
686
687 p.discardLineWhitespace();
688 assertPosition(p, 2, 1);
689 assertChar('\r', p.readChar());
690
691 p.discardLineWhitespace();
692 assertPosition(p, 2, 2);
693 assertChar('\n', p.readChar());
694
695 p.discardLineWhitespace();
696 assertPosition(p, 3, 4);
697 assertChar('b', p.readChar());
698
699 p.discardLineWhitespace();
700 assertPosition(p, 3, 6);
701 assertChar('c', p.readChar());
702
703 p.discardLineWhitespace();
704 assertPosition(p, 3, 7);
705 assertChar(EOF, p.readChar());
706 }
707
708 @Test
709 void testDiscardNewLineSequence() {
710
711 final SimpleTextParser p = parser("a\t\n\r\n b\rc");
712
713
714 p.discardNewLineSequence();
715 assertPosition(p, 1, 1);
716 assertChar('a', p.readChar());
717
718 p.discardLineWhitespace();
719
720 p.discardNewLineSequence();
721 assertPosition(p, 2, 1);
722 assertChar('\r', p.readChar());
723
724 p.discardNewLineSequence();
725 assertPosition(p, 3, 1);
726 assertChar(' ', p.readChar());
727
728 p.discardWhitespace();
729
730 p.discardNewLineSequence();
731 assertPosition(p, 3, 4);
732 assertChar('b', p.readChar());
733
734 p.discardNewLineSequence();
735 assertPosition(p, 4, 1);
736 assertChar('c', p.readChar());
737
738 p.discardNewLineSequence();
739 assertPosition(p, 4, 2);
740 assertChar(EOF, p.readChar());
741 }
742
743 @Test
744 void testDiscardLine() {
745
746 final SimpleTextParser p = parser("a\t\n\r\n b c");
747
748
749 p.discardLine();
750 assertChar('\r', p.peekChar());
751 assertPosition(p, 2, 1);
752
753 p.discardLine();
754 assertChar(' ', p.peekChar());
755 assertPosition(p, 3, 1);
756
757 p.discardLine();
758 assertPosition(p, 3, 7);
759 assertChar(EOF, p.peekChar());
760
761 p.discardLine();
762 assertPosition(p, 3, 7);
763 assertChar(EOF, p.peekChar());
764 }
765
766 @Test
767 void testPeek_lenArg() {
768
769 final SimpleTextParser p = parser("abcdef\r\n\r ghi");
770
771
772 Assertions.assertEquals("", p.peek(0));
773 assertPosition(p, 1, 1);
774
775 Assertions.assertEquals("", p.peek(0));
776 assertPosition(p, 1, 1);
777
778 p.readChar();
779
780 Assertions.assertEquals("bcde", p.peek(4));
781 assertPosition(p, 1, 2);
782
783 Assertions.assertEquals("bcdef\r", p.peek(6));
784 assertPosition(p, 1, 2);
785
786 Assertions.assertEquals("bcdef\r\n\r ghi", p.peek(100));
787 assertPosition(p, 1, 2);
788
789 assertChar('b', p.readChar());
790
791 p.discard(c -> true);
792
793 Assertions.assertNull(p.peek(0));
794 Assertions.assertNull(p.peek(100));
795 }
796
797 @Test
798 void testPeek_lenArg_invalidArg() {
799
800 final SimpleTextParser p = parser("abcdef");
801 p.setMaxStringLength(4);
802
803
804 GeometryTestUtils.assertThrowsWithMessage(() -> {
805 p.peek(-1);
806 }, IllegalArgumentException.class, "Requested string length cannot be negative; was -1");
807
808 GeometryTestUtils.assertThrowsWithMessage(() -> {
809 p.peek(6);
810 }, IllegalArgumentException.class, "Requested string length of 6 exceeds maximum value of 4");
811 }
812
813 @Test
814 void testPeek_predicateArg() {
815
816 final SimpleTextParser p = parser("abcdef\r\n\r ghi");
817
818
819 Assertions.assertEquals("", p.peek(c -> false));
820 assertPosition(p, 1, 1);
821
822 p.readChar();
823
824 Assertions.assertEquals("bcdef", p.peek(SimpleTextParser::isAlphanumeric));
825 assertPosition(p, 1, 2);
826
827 Assertions.assertEquals("bcdef\r\n\r ghi", p.peek(c -> true));
828 assertPosition(p, 1, 2);
829
830 assertChar('b', p.readChar());
831
832 p.discard(c -> true);
833
834 Assertions.assertNull(p.peek(c -> true));
835 Assertions.assertNull(p.peek(c -> false));
836 }
837
838 @Test
839 void testPeek_predicateArg_exceedsMaxStringLength() {
840
841 final SimpleTextParser p = parser("\n abcdefg");
842 p.setMaxStringLength(4);
843 p.discardLine()
844 .discard(SimpleTextParser::isWhitespace);
845
846
847 GeometryTestUtils.assertThrowsWithMessage(() -> {
848 p.peek(SimpleTextParser::isNotWhitespace);
849 }, IllegalStateException.class, "Parsing failed at line 2, column 3: string length exceeds maximum value of 4");
850 }
851
852 @Test
853 void testMatch() {
854
855 final SimpleTextParser p = parser("abcdef");
856
857
858 p.next(1)
859 .match("a")
860 .next(100)
861 .match("bcdef");
862
863 Assertions.assertFalse(p.hasMoreCharacters());
864 }
865
866 @Test
867 void testMatch_failure() {
868
869 final SimpleTextParser p = parser("abcdef");
870
871
872 GeometryTestUtils.assertThrowsWithMessage(() -> {
873 p.match("empty");
874 }, IllegalStateException.class, "No token has been read from the character stream");
875
876 p.next(1);
877 GeometryTestUtils.assertThrowsWithMessage(() -> {
878 p.match("b");
879 }, IllegalStateException.class, "Parsing failed at line 1, column 1: expected [b] but found [a]");
880
881 GeometryTestUtils.assertThrowsWithMessage(() -> {
882 p.match("A");
883 }, IllegalStateException.class, "Parsing failed at line 1, column 1: expected [A] but found [a]");
884
885 GeometryTestUtils.assertThrowsWithMessage(() -> {
886 p.match(null);
887 }, IllegalStateException.class, "Parsing failed at line 1, column 1: expected [null] but found [a]");
888 }
889
890 @Test
891 void testMatch_ignoreCase() {
892
893 final SimpleTextParser p = parser("abcdef");
894
895
896 p.next(1)
897 .matchIgnoreCase("A")
898 .next(100)
899 .matchIgnoreCase("BcdEF");
900
901 Assertions.assertFalse(p.hasMoreCharacters());
902 }
903
904 @Test
905 void testMatchIgnoreCase_failure() {
906
907 final SimpleTextParser p = parser("abcdef");
908
909
910 GeometryTestUtils.assertThrowsWithMessage(() -> {
911 p.matchIgnoreCase("empty");
912 }, IllegalStateException.class, "No token has been read from the character stream");
913
914 p.next(1);
915 GeometryTestUtils.assertThrowsWithMessage(() -> {
916 p.matchIgnoreCase("b");
917 }, IllegalStateException.class, "Parsing failed at line 1, column 1: expected [b] but found [a]");
918
919 GeometryTestUtils.assertThrowsWithMessage(() -> {
920 p.match(null);
921 }, IllegalStateException.class, "Parsing failed at line 1, column 1: expected [null] but found [a]");
922 }
923
924 @Test
925 void testTryMatch() {
926
927 final SimpleTextParser p = parser("abc");
928
929
930 p.next(3);
931
932 Assertions.assertTrue(p.tryMatch("abc"));
933
934 Assertions.assertFalse(p.tryMatch("ab"));
935 Assertions.assertFalse(p.tryMatch(""));
936 Assertions.assertFalse(p.tryMatch(null));
937
938 Assertions.assertFalse(p.tryMatch("ABC"));
939 Assertions.assertFalse(p.tryMatch("aBc"));
940
941 p.next(1);
942 Assertions.assertTrue(p.tryMatch(null));
943 }
944
945 @Test
946 void testTryMatch_noToken() {
947
948 final SimpleTextParser p = parser("abcdef");
949
950
951 GeometryTestUtils.assertThrowsWithMessage(() -> {
952 p.tryMatch("empty");
953 }, IllegalStateException.class, "No token has been read from the character stream");
954 }
955
956 @Test
957 void testTryMatchIgnoreCase() {
958
959 final SimpleTextParser p = parser("abc");
960
961
962 p.next(3);
963
964 Assertions.assertTrue(p.tryMatchIgnoreCase("abc"));
965 Assertions.assertTrue(p.tryMatchIgnoreCase("ABC"));
966 Assertions.assertTrue(p.tryMatchIgnoreCase("aBc"));
967
968 Assertions.assertFalse(p.tryMatch("ab"));
969 Assertions.assertFalse(p.tryMatch(""));
970 Assertions.assertFalse(p.tryMatch(null));
971
972 p.next(1);
973 Assertions.assertTrue(p.tryMatch(null));
974 }
975
976 @Test
977 void testTryMatchIgnoreCase_noToken() {
978
979 final SimpleTextParser p = parser("abcdef");
980
981
982 GeometryTestUtils.assertThrowsWithMessage(() -> {
983 p.tryMatchIgnoreCase("empty");
984 }, IllegalStateException.class, "No token has been read from the character stream");
985 }
986
987 @Test
988 void testChoose() {
989
990 final SimpleTextParser p = parser("abc");
991
992
993 p.next(1);
994
995 Assertions.assertEquals(0, p.choose("a"));
996
997 Assertions.assertEquals(0, p.choose("a", "b", "c"));
998 Assertions.assertEquals(2, p.choose("c", "b", "a"));
999
1000 p.next(1);
1001
1002 Assertions.assertEquals(0, p.choose("b"));
1003
1004 Assertions.assertEquals(1, p.choose("a", "b", "c"));
1005 Assertions.assertEquals(1, p.choose("c", "b", "a"));
1006 }
1007
1008 @Test
1009 void testChoose_failure() {
1010
1011 final SimpleTextParser p = parser("abc");
1012
1013
1014 GeometryTestUtils.assertThrowsWithMessage(() -> {
1015 p.choose("X");
1016 }, IllegalStateException.class, "No token has been read from the character stream");
1017
1018 p.next(1);
1019 GeometryTestUtils.assertThrowsWithMessage(() -> {
1020 p.choose("X");
1021 }, IllegalStateException.class, "Parsing failed at line 1, column 1: expected one of [X] but found [a]");
1022
1023 GeometryTestUtils.assertThrowsWithMessage(() -> {
1024 p.choose("X", "Y", "Z");
1025 }, IllegalStateException.class, "Parsing failed at line 1, column 1: expected one of [X, Y, Z] but found [a]");
1026
1027 GeometryTestUtils.assertThrowsWithMessage(() -> {
1028 p.choose("A");
1029 }, IllegalStateException.class, "Parsing failed at line 1, column 1: expected one of [A] but found [a]");
1030
1031 GeometryTestUtils.assertThrowsWithMessage(() -> {
1032 p.choose();
1033 }, IllegalStateException.class, "Parsing failed at line 1, column 1: expected one of [] but found [a]");
1034 }
1035
1036 @Test
1037 void testChooseIgnoreCase() {
1038
1039 final SimpleTextParser p = parser("abc");
1040
1041
1042 p.next(1);
1043
1044 Assertions.assertEquals(0, p.chooseIgnoreCase("A"));
1045
1046 Assertions.assertEquals(0, p.chooseIgnoreCase("A", "b", "C"));
1047 Assertions.assertEquals(2, p.chooseIgnoreCase("C", "b", "A"));
1048
1049 p.next(1);
1050
1051 Assertions.assertEquals(0, p.chooseIgnoreCase("b"));
1052
1053 Assertions.assertEquals(1, p.chooseIgnoreCase("A", "b", "C"));
1054 Assertions.assertEquals(1, p.chooseIgnoreCase("C", "b", "A"));
1055 }
1056
1057 @Test
1058 void testChooseIgnoreCase_failure() {
1059
1060 final SimpleTextParser p = parser("abc");
1061
1062
1063 GeometryTestUtils.assertThrowsWithMessage(() -> {
1064 p.chooseIgnoreCase("X");
1065 }, IllegalStateException.class, "No token has been read from the character stream");
1066
1067 p.next(1);
1068 GeometryTestUtils.assertThrowsWithMessage(() -> {
1069 p.chooseIgnoreCase("X");
1070 }, IllegalStateException.class, "Parsing failed at line 1, column 1: expected one of [X] but found [a]");
1071
1072 GeometryTestUtils.assertThrowsWithMessage(() -> {
1073 p.chooseIgnoreCase("X", "Y", "Z");
1074 }, IllegalStateException.class, "Parsing failed at line 1, column 1: expected one of [X, Y, Z] but found [a]");
1075
1076 GeometryTestUtils.assertThrowsWithMessage(() -> {
1077 p.chooseIgnoreCase();
1078 }, IllegalStateException.class, "Parsing failed at line 1, column 1: expected one of [] but found [a]");
1079 }
1080
1081 @Test
1082 void testTryChoose() {
1083
1084 final SimpleTextParser p = parser("abc");
1085
1086
1087 p.next(1);
1088
1089 Assertions.assertEquals(0, p.tryChoose("a"));
1090
1091 Assertions.assertEquals(0, p.tryChoose("a", "b", "c"));
1092 Assertions.assertEquals(2, p.tryChoose("c", "b", "a"));
1093
1094 p.next(1);
1095
1096 Assertions.assertEquals(0, p.tryChoose("b"));
1097
1098 Assertions.assertEquals(1, p.tryChoose("a", "b", "c"));
1099 Assertions.assertEquals(1, p.tryChoose("c", "b", "a"));
1100
1101 Assertions.assertEquals(-1, p.tryChoose("A", "B", "C"));
1102 Assertions.assertEquals(-1, p.tryChoose());
1103 Assertions.assertEquals(-1, p.tryChoose((String) null));
1104 }
1105
1106 @Test
1107 void testTryChoose_noToken() {
1108
1109 final SimpleTextParser p = parser("abcdef");
1110
1111
1112 GeometryTestUtils.assertThrowsWithMessage(() -> {
1113 p.tryChoose("X");
1114 }, IllegalStateException.class, "No token has been read from the character stream");
1115 }
1116
1117 @Test
1118 void testTryChooseIgnoreCase() {
1119
1120 final SimpleTextParser p = parser("abc");
1121
1122
1123 p.next(1);
1124
1125 Assertions.assertEquals(0, p.tryChooseIgnoreCase("a"));
1126
1127 Assertions.assertEquals(0, p.tryChooseIgnoreCase("A", "B", "C"));
1128 Assertions.assertEquals(2, p.tryChooseIgnoreCase("C", "b", "A"));
1129
1130 p.next(1);
1131
1132 Assertions.assertEquals(0, p.tryChooseIgnoreCase("B"));
1133
1134 Assertions.assertEquals(1, p.tryChooseIgnoreCase("a", "B", "c"));
1135 Assertions.assertEquals(1, p.tryChooseIgnoreCase("c", "b", "a"));
1136
1137 Assertions.assertEquals(-1, p.tryChooseIgnoreCase("X", "Y", "Z"));
1138 Assertions.assertEquals(-1, p.tryChooseIgnoreCase());
1139 Assertions.assertEquals(-1, p.tryChooseIgnoreCase((String) null));
1140 }
1141
1142 @Test
1143 void testTryChooseIgnoreCase_noToken() {
1144
1145 final SimpleTextParser p = parser("abcdef");
1146
1147
1148 GeometryTestUtils.assertThrowsWithMessage(() -> {
1149 p.tryChooseIgnoreCase("X");
1150 }, IllegalStateException.class, "No token has been read from the character stream");
1151 }
1152
1153 @Test
1154 void testUnexpectedToken() {
1155
1156 final SimpleTextParser p = parser("abc\ndef");
1157
1158
1159 Assertions.assertEquals("Parsing failed at line 1, column 1: expected test but found no current token",
1160 p.unexpectedToken("test").getMessage());
1161
1162 p.nextAlphanumeric();
1163 Assertions.assertEquals("Parsing failed at line 1, column 1: expected test but found [abc]",
1164 p.unexpectedToken("test").getMessage());
1165
1166 p.nextAlphanumeric();
1167 Assertions.assertEquals("Parsing failed at line 1, column 4: expected test but found end of line",
1168 p.unexpectedToken("test").getMessage());
1169
1170 p.discardLine();
1171
1172 p.next(SimpleTextParser::isWhitespace);
1173 Assertions.assertEquals("Parsing failed at line 2, column 1: expected test but found empty token followed by [d]",
1174 p.unexpectedToken("test").getMessage());
1175
1176 p.next(3).next(10);
1177 Assertions.assertEquals("Parsing failed at line 2, column 4: expected test but found end of content",
1178 p.unexpectedToken("test").getMessage());
1179 }
1180
1181 @Test
1182 void testUnexpectedToken_causeArg() {
1183
1184 final SimpleTextParser p = parser("abc");
1185 final Exception cause = new Exception("test");
1186
1187
1188 p.nextLine();
1189
1190 final IllegalStateException exc = p.unexpectedToken("test", cause);
1191 Assertions.assertEquals("Parsing failed at line 1, column 1: expected test but found [abc]",
1192 exc.getMessage());
1193 Assertions.assertSame(cause, exc.getCause());
1194 }
1195
1196 @Test
1197 void testUnexpectedToken_ioError() {
1198
1199 final FailBuffer b = new FailBuffer(new StringReader("abc"));
1200 final SimpleTextParser p = new SimpleTextParser(b);
1201
1202
1203 b.setFail(false);
1204 p.next(SimpleTextParser::isDecimalPart);
1205 b.setFail(true);
1206 Assertions.assertEquals("Parsing failed at line 1, column 1: expected test but found empty token",
1207 p.unexpectedToken("test").getMessage());
1208
1209 b.setFail(false);
1210 p.nextAlphanumeric();
1211 b.setFail(true);
1212 Assertions.assertEquals("Parsing failed at line 1, column 1: expected test but found [abc]",
1213 p.unexpectedToken("test").getMessage());
1214
1215 b.setFail(false);
1216 p.nextAlphanumeric();
1217 b.setFail(true);
1218 Assertions.assertEquals("Parsing failed at line 1, column 4: expected test but found no current token",
1219 p.unexpectedToken("test").getMessage());
1220 }
1221
1222 @Test
1223 void testTokenError() {
1224
1225 final SimpleTextParser p = parser("a\nbc");
1226 p.nextLine();
1227 p.next(1);
1228 p.readChar();
1229
1230
1231 final IllegalStateException exc = p.tokenError("test message");
1232
1233 Assertions.assertEquals("Parsing failed at line 2, column 1: test message", exc.getMessage());
1234 Assertions.assertNull(exc.getCause());
1235 }
1236
1237 @Test
1238 void testTokenError_noTokenSet() {
1239
1240 final SimpleTextParser p = parser("ab\nc");
1241 p.readChar();
1242
1243
1244 final IllegalStateException exc = p.tokenError("test message");
1245
1246 Assertions.assertEquals("Parsing failed at line 1, column 2: test message", exc.getMessage());
1247 Assertions.assertNull(exc.getCause());
1248 }
1249
1250 @Test
1251 void testTokenError_withCause() {
1252
1253 SimpleTextParser p = parser("a\nbc");
1254 p.nextLine();
1255 p.next(1);
1256 p.readChar();
1257
1258 final Exception cause = new Exception("test");
1259
1260
1261 final IllegalStateException exc = p.tokenError("test message", cause);
1262
1263 Assertions.assertEquals("Parsing failed at line 2, column 1: test message", exc.getMessage());
1264 Assertions.assertSame(cause, exc.getCause());
1265 }
1266
1267 @Test
1268 void testParseError_currentLineCol() {
1269
1270 final SimpleTextParser p = parser("a\nbc");
1271 p.discard(ch -> ch != 'b');
1272
1273
1274 final IllegalStateException exc = p.parseError("test message");
1275
1276 Assertions.assertEquals("Parsing failed at line 2, column 1: test message", exc.getMessage());
1277 Assertions.assertNull(exc.getCause());
1278 }
1279
1280 @Test
1281 void testParseError_currentLineCol_withCause() {
1282
1283 final SimpleTextParser p = parser("abc");
1284 p.readChar();
1285 final Exception cause = new Exception("test");
1286
1287
1288 final IllegalStateException exc = p.parseError("test message", cause);
1289
1290 Assertions.assertEquals("Parsing failed at line 1, column 2: test message", exc.getMessage());
1291 Assertions.assertSame(cause, exc.getCause());
1292 }
1293
1294 @Test
1295 void testParseError_givenLineCol() {
1296
1297 final SimpleTextParser p = parser("abc");
1298
1299
1300 final IllegalStateException exc = p.parseError(5, 6, "test message");
1301
1302 Assertions.assertEquals("Parsing failed at line 5, column 6: test message", exc.getMessage());
1303 Assertions.assertNull(exc.getCause());
1304 }
1305
1306 @Test
1307 void testParseError_givenLineCol_withCause() {
1308
1309 final SimpleTextParser p = parser("abc");
1310 final Exception cause = new Exception("test");
1311
1312
1313 final IllegalStateException exc = p.parseError(5, 6, "test message", cause);
1314
1315 Assertions.assertEquals("Parsing failed at line 5, column 6: test message", exc.getMessage());
1316 Assertions.assertSame(cause, exc.getCause());
1317 }
1318
1319 @Test
1320 void testCharacterPredicates() {
1321
1322 assertMatchesAll(SimpleTextParser::isWhitespace, " \t\n\r");
1323 assertDoesNotMatchAny(SimpleTextParser::isWhitespace, "abcABC<>,./?:;'\"[]{}`~!@#$%^&*()_+-=");
1324
1325 assertMatchesAll(SimpleTextParser::isNotWhitespace, "abcABC<>,./?:;'\"[]{}`~!@#$%^&*()_+-=");
1326 assertDoesNotMatchAny(SimpleTextParser::isNotWhitespace, " \t\n\r");
1327
1328 assertMatchesAll(SimpleTextParser::isLineWhitespace, " \t");
1329 assertDoesNotMatchAny(SimpleTextParser::isLineWhitespace, "\n\rabcABC<>,./?:;'\"[]{}`~!@#$%^&*()_+-=");
1330
1331 assertMatchesAll(SimpleTextParser::isNewLinePart, "\n\r");
1332 assertDoesNotMatchAny(SimpleTextParser::isNewLinePart, " \tabcABC<>,./?:;'\"[]{}`~!@#$%^&*()_+-=");
1333
1334 assertMatchesAll(SimpleTextParser::isNotNewLinePart, " \tabcABC<>,./?:;'\"[]{}`~!@#$%^&*()_+-=");
1335 assertDoesNotMatchAny(SimpleTextParser::isNotNewLinePart, "\n\r");
1336
1337 assertMatchesAll(SimpleTextParser::isAlphanumeric, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
1338 assertDoesNotMatchAny(SimpleTextParser::isAlphanumeric, " \t\n\r./?:;'\\\"[]{}`~!@#$%^&*()_+-=");
1339
1340 assertMatchesAll(SimpleTextParser::isNotAlphanumeric, " \t\n\r./?:;'\\\"[]{}`~!@#$%^&*()_+-=");
1341 assertDoesNotMatchAny(SimpleTextParser::isNotAlphanumeric, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
1342
1343 assertMatchesAll(SimpleTextParser::isIntegerPart, "0123456789+-");
1344 assertDoesNotMatchAny(SimpleTextParser::isIntegerPart, " \t\n\r./?:;'\\\"[]{}`~!@#$%^&*()_=abcdeABCDE");
1345
1346 assertMatchesAll(SimpleTextParser::isDecimalPart, "0123456789+-.eE");
1347 assertDoesNotMatchAny(SimpleTextParser::isDecimalPart, " \t\n\r/?:;'\\\"[]{}`~!@#$%^&*()_=abcdABCD");
1348 }
1349
1350 private static SimpleTextParser parser(final String content) {
1351 final StringReader reader = new StringReader(content);
1352
1353 return new SimpleTextParser(reader);
1354 }
1355
1356 private static void assertCharacterSequence(final SimpleTextParser parser, final String expected) {
1357 char expectedChar;
1358 String msg;
1359 for (int i = 0; i < expected.length(); ++i) {
1360 expectedChar = expected.charAt(i);
1361
1362 msg = "Failed at index " + i + ":";
1363
1364 Assertions.assertEquals(expectedChar, parser.peekChar(), msg);
1365 Assertions.assertEquals(expectedChar, parser.peekChar(), msg);
1366
1367 Assertions.assertTrue(parser.hasMoreCharacters());
1368 Assertions.assertEquals(expectedChar, parser.readChar(), msg);
1369 }
1370
1371 Assertions.assertFalse(parser.hasMoreCharacters());
1372 Assertions.assertEquals(-1, parser.peekChar());
1373 Assertions.assertEquals(-1, parser.peekChar());
1374 Assertions.assertEquals(-1, parser.readChar());
1375 }
1376
1377 private static void assertChar(final int expected, final int actual) {
1378 final String expectedStr = describeChar(expected);
1379 final String actualStr = describeChar(actual);
1380
1381 Assertions.assertEquals(expected, actual, "Expected [" + expectedStr + "] but was [" + actualStr + "];");
1382 }
1383
1384 private static void assertMatchesAll(final IntPredicate pred, final String chars) {
1385 for (char ch : chars.toCharArray()) {
1386 final String msg = "Expected predicate to match [" + describeChar(ch) + "]";
1387 Assertions.assertTrue(pred.test(ch), msg);
1388 }
1389 }
1390
1391 private static void assertDoesNotMatchAny(final IntPredicate pred, final String chars) {
1392 for (char ch : chars.toCharArray()) {
1393 final String msg = "Expected predicate to not match [" + describeChar(ch) + "]";
1394 Assertions.assertFalse(pred.test(ch), msg);
1395 }
1396 }
1397
1398 private static String describeChar(final int ch) {
1399 switch (ch) {
1400 case '\n':
1401 return "\\n";
1402 case '\r':
1403 return "\\r";
1404 case '\t':
1405 return "\\t";
1406 case EOF:
1407 return "EOF";
1408 default:
1409 return String.valueOf((char) ch);
1410 }
1411 }
1412
1413 private static void assertPosition(final SimpleTextParser parser, final int line, final int col) {
1414 Assertions.assertEquals(line, parser.getLineNumber(), "Unexpected line number");
1415 Assertions.assertEquals(col, parser.getColumnNumber(), "Unexpected column number");
1416 }
1417
1418 private static void assertToken(final SimpleTextParser parser, final String token, final int line, final int col) {
1419 Assertions.assertEquals(token, parser.getCurrentToken(), "Unexpected token");
1420 Assertions.assertEquals(line, parser.getCurrentTokenLineNumber(), "Unexpected token line number");
1421 Assertions.assertEquals(col, parser.getCurrentTokenColumnNumber(), "Unexpected token column number");
1422 }
1423
1424 private static final class FailBuffer extends CharReadBuffer {
1425
1426 private boolean fail;
1427
1428 FailBuffer(final Reader in) {
1429 super(in);
1430 }
1431
1432 public void setFail(final boolean fail) {
1433 this.fail = fail;
1434 }
1435
1436 @Override
1437 public boolean hasMoreCharacters() {
1438 checkFail();
1439 return super.hasMoreCharacters();
1440 }
1441
1442 private void checkFail() {
1443 if (fail) {
1444 throw new IllegalStateException("test failure");
1445 }
1446 }
1447 }
1448 }