1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package org.apache.jasper.xmlparser;
27
28 import java.io.EOFException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.io.IOException;
32 import java.io.Reader;
33 import java.util.Locale;
34 import java.util.jar.JarFile;
35
36 import org.apache.jasper.JasperException;
37 import org.apache.jasper.JspCompilationContext;
38 import org.apache.jasper.compiler.ErrorDispatcher;
39 import org.apache.jasper.compiler.JspUtil;
40
41 public class XMLEncodingDetector {
42
43 private InputStream stream;
44 private String encoding;
45 private boolean isEncodingSetInProlog;
46 private boolean isBomPresent;
47 private int skip;
48 private Boolean isBigEndian;
49 private Reader reader;
50
51
52 public static final int DEFAULT_BUFFER_SIZE = 2048;
53 public static final int DEFAULT_XMLDECL_BUFFER_SIZE = 64;
54 private boolean fAllowJavaEncodings;
55 private SymbolTable fSymbolTable;
56 private XMLEncodingDetector fCurrentEntity;
57 private int fBufferSize = DEFAULT_BUFFER_SIZE;
58
59
60 private int lineNumber = 1;
61 private int columnNumber = 1;
62 private boolean literal;
63 private char[] ch = new char[DEFAULT_BUFFER_SIZE];
64 private int position;
65 private int count;
66 private boolean mayReadChunks = false;
67
68
69 private XMLString fString = new XMLString();
70 private XMLStringBuffer fStringBuffer = new XMLStringBuffer();
71 private XMLStringBuffer fStringBuffer2 = new XMLStringBuffer();
72 private final static String fVersionSymbol = "version";
73 private final static String fEncodingSymbol = "encoding";
74 private final static String fStandaloneSymbol = "standalone";
75
76
77 private int fMarkupDepth = 0;
78 private String[] fStrings = new String[3];
79
80 private ErrorDispatcher err;
81
82
83
84
85 public XMLEncodingDetector() {
86 fSymbolTable = new SymbolTable();
87 fCurrentEntity = this;
88 }
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 public static Object[] getEncoding(String fname, JarFile jarFile,
104 JspCompilationContext ctxt,
105 ErrorDispatcher err)
106 throws IOException, JasperException
107 {
108 InputStream inStream = JspUtil.getInputStream(fname, jarFile, ctxt,
109 err);
110 XMLEncodingDetector detector = new XMLEncodingDetector();
111 Object[] ret = detector.getEncoding(inStream, err);
112 inStream.close();
113
114 return ret;
115 }
116
117 private Object[] getEncoding(InputStream in, ErrorDispatcher err)
118 throws IOException, JasperException
119 {
120 this.stream = in;
121 this.err=err;
122 createInitialReader();
123 scanXMLDecl();
124
125 return new Object[] { this.encoding,
126 Boolean.valueOf(this.isEncodingSetInProlog),
127 Boolean.valueOf(this.isBomPresent),
128 Integer.valueOf(this.skip) };
129 }
130
131
132 void endEntity() {
133 }
134
135
136
137 private void createInitialReader() throws IOException, JasperException {
138
139
140 stream = new RewindableInputStream(stream);
141
142
143 if (encoding == null) {
144
145 final byte[] b4 = new byte[4];
146 int count = 0;
147 for (; count<4; count++ ) {
148 b4[count] = (byte)stream.read();
149 }
150 if (count == 4) {
151 Object [] encodingDesc = getEncodingName(b4, count);
152 encoding = (String)(encodingDesc[0]);
153 isBigEndian = (Boolean)(encodingDesc[1]);
154
155 if (encodingDesc.length > 3) {
156 isBomPresent = (Boolean)(encodingDesc[2]);
157 skip = (Integer)(encodingDesc[3]);
158 } else {
159 isBomPresent = true;
160 skip = (Integer)(encodingDesc[2]);
161 }
162
163 stream.reset();
164
165
166
167 if (count > 2 && encoding.equals("UTF-8")) {
168 int b0 = b4[0] & 0xFF;
169 int b1 = b4[1] & 0xFF;
170 int b2 = b4[2] & 0xFF;
171 if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
172
173 stream.skip(3);
174 }
175 }
176 reader = createReader(stream, encoding, isBigEndian);
177 } else {
178 reader = createReader(stream, encoding, isBigEndian);
179 }
180 }
181 }
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201 private Reader createReader(InputStream inputStream, String encoding,
202 Boolean isBigEndian)
203 throws IOException, JasperException {
204
205
206 if (encoding == null) {
207 encoding = "UTF-8";
208 }
209
210
211 String ENCODING = encoding.toUpperCase(Locale.ENGLISH);
212 if (ENCODING.equals("UTF-8")) {
213 return new UTF8Reader(inputStream, fBufferSize);
214 }
215 if (ENCODING.equals("US-ASCII")) {
216 return new ASCIIReader(inputStream, fBufferSize);
217 }
218 if (ENCODING.equals("ISO-10646-UCS-4")) {
219 if (isBigEndian != null) {
220 boolean isBE = isBigEndian.booleanValue();
221 if (isBE) {
222 return new UCSReader(inputStream, UCSReader.UCS4BE);
223 } else {
224 return new UCSReader(inputStream, UCSReader.UCS4LE);
225 }
226 } else {
227 err.jspError("jsp.error.xml.encodingByteOrderUnsupported",
228 encoding);
229 }
230 }
231 if (ENCODING.equals("ISO-10646-UCS-2")) {
232 if (isBigEndian != null) {
233 boolean isBE = isBigEndian.booleanValue();
234 if (isBE) {
235 return new UCSReader(inputStream, UCSReader.UCS2BE);
236 } else {
237 return new UCSReader(inputStream, UCSReader.UCS2LE);
238 }
239 } else {
240 err.jspError("jsp.error.xml.encodingByteOrderUnsupported",
241 encoding);
242 }
243 }
244
245
246 boolean validIANA = XMLChar.isValidIANAEncoding(encoding);
247 boolean validJava = XMLChar.isValidJavaEncoding(encoding);
248 if (!validIANA || (fAllowJavaEncodings && !validJava)) {
249 err.jspError("jsp.error.xml.encodingDeclInvalid", encoding);
250
251
252
253
254
255
256
257
258 encoding = "ISO-8859-1";
259 }
260
261
262 String javaEncoding = EncodingMap.getIANA2JavaMapping(ENCODING);
263 if (javaEncoding == null) {
264 if (fAllowJavaEncodings) {
265 javaEncoding = encoding;
266 } else {
267 err.jspError("jsp.error.xml.encodingDeclInvalid", encoding);
268
269 javaEncoding = "ISO8859_1";
270 }
271 }
272 return new InputStreamReader(inputStream, javaEncoding);
273
274 }
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290 private Object[] getEncodingName(byte[] b4, int count) {
291
292 if (count < 2) {
293 return new Object[]{"UTF-8", null, Boolean.FALSE, Integer.valueOf(0)};
294 }
295
296
297 int b0 = b4[0] & 0xFF;
298 int b1 = b4[1] & 0xFF;
299 if (b0 == 0xFE && b1 == 0xFF) {
300
301 return new Object [] {"UTF-16BE", Boolean.TRUE, Integer.valueOf(2)};
302 }
303 if (b0 == 0xFF && b1 == 0xFE) {
304
305 return new Object [] {"UTF-16LE", Boolean.FALSE, Integer.valueOf(2)};
306 }
307
308
309
310 if (count < 3) {
311 return new Object [] {"UTF-8", null, Boolean.FALSE, Integer.valueOf(0)};
312 }
313
314
315 int b2 = b4[2] & 0xFF;
316 if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
317 return new Object [] {"UTF-8", null, Integer.valueOf(3)};
318 }
319
320
321
322 if (count < 4) {
323 return new Object [] {"UTF-8", null, Integer.valueOf(0)};
324 }
325
326
327 int b3 = b4[3] & 0xFF;
328 if (b0 == 0x00 && b1 == 0x00 && b2 == 0x00 && b3 == 0x3C) {
329
330 return new Object [] {"ISO-10646-UCS-4", new Boolean(true), Integer.valueOf(4)};
331 }
332 if (b0 == 0x3C && b1 == 0x00 && b2 == 0x00 && b3 == 0x00) {
333
334 return new Object [] {"ISO-10646-UCS-4", new Boolean(false), Integer.valueOf(4)};
335 }
336 if (b0 == 0x00 && b1 == 0x00 && b2 == 0x3C && b3 == 0x00) {
337
338
339 return new Object [] {"ISO-10646-UCS-4", null, Integer.valueOf(4)};
340 }
341 if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x00) {
342
343
344 return new Object [] {"ISO-10646-UCS-4", null, Integer.valueOf(4)};
345 }
346 if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) {
347
348
349
350 return new Object [] {"UTF-16BE", new Boolean(true), Integer.valueOf(4)};
351 }
352 if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) {
353
354
355 return new Object [] {"UTF-16LE", new Boolean(false), Integer.valueOf(4)};
356 }
357 if (b0 == 0x4C && b1 == 0x6F && b2 == 0xA7 && b3 == 0x94) {
358
359
360 return new Object [] {"CP037", null, Integer.valueOf(4)};
361 }
362
363
364 return new Object [] {"UTF-8", null, Boolean.FALSE, Integer.valueOf(0)};
365
366 }
367
368
369
370
371 public boolean isExternal() {
372 return true;
373 }
374
375
376
377
378
379
380
381
382
383
384
385 public int peekChar() throws IOException {
386
387
388 if (fCurrentEntity.position == fCurrentEntity.count) {
389 load(0, true);
390 }
391
392
393 int c = fCurrentEntity.ch[fCurrentEntity.position];
394
395
396 if (fCurrentEntity.isExternal()) {
397 return c != '\r' ? c : '\n';
398 }
399 else {
400 return c;
401 }
402
403 }
404
405
406
407
408
409
410
411
412
413
414
415 public int scanChar() throws IOException {
416
417
418 if (fCurrentEntity.position == fCurrentEntity.count) {
419 load(0, true);
420 }
421
422
423 int c = fCurrentEntity.ch[fCurrentEntity.position++];
424 boolean external = false;
425 if (c == '\n' ||
426 (c == '\r' && (external = fCurrentEntity.isExternal()))) {
427 fCurrentEntity.lineNumber++;
428 fCurrentEntity.columnNumber = 1;
429 if (fCurrentEntity.position == fCurrentEntity.count) {
430 fCurrentEntity.ch[0] = (char)c;
431 load(1, false);
432 }
433 if (c == '\r' && external) {
434 if (fCurrentEntity.ch[fCurrentEntity.position++] != '\n') {
435 fCurrentEntity.position--;
436 }
437 c = '\n';
438 }
439 }
440
441
442 fCurrentEntity.columnNumber++;
443 return c;
444
445 }
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465 public String scanName() throws IOException {
466
467
468 if (fCurrentEntity.position == fCurrentEntity.count) {
469 load(0, true);
470 }
471
472
473 int offset = fCurrentEntity.position;
474 if (XMLChar.isNameStart(fCurrentEntity.ch[offset])) {
475 if (++fCurrentEntity.position == fCurrentEntity.count) {
476 fCurrentEntity.ch[0] = fCurrentEntity.ch[offset];
477 offset = 0;
478 if (load(1, false)) {
479 fCurrentEntity.columnNumber++;
480 String symbol = fSymbolTable.addSymbol(fCurrentEntity.ch,
481 0, 1);
482 return symbol;
483 }
484 }
485 while (XMLChar.isName(fCurrentEntity.ch[fCurrentEntity.position])) {
486 if (++fCurrentEntity.position == fCurrentEntity.count) {
487 int length = fCurrentEntity.position - offset;
488 if (length == fBufferSize) {
489
490 char[] tmp = new char[fBufferSize * 2];
491 System.arraycopy(fCurrentEntity.ch, offset,
492 tmp, 0, length);
493 fCurrentEntity.ch = tmp;
494 fBufferSize *= 2;
495 } else {
496 System.arraycopy(fCurrentEntity.ch, offset,
497 fCurrentEntity.ch, 0, length);
498 }
499 offset = 0;
500 if (load(length, false)) {
501 break;
502 }
503 }
504 }
505 }
506 int length = fCurrentEntity.position - offset;
507 fCurrentEntity.columnNumber += length;
508
509
510 String symbol = null;
511 if (length > 0) {
512 symbol = fSymbolTable.addSymbol(fCurrentEntity.ch, offset, length);
513 }
514 return symbol;
515
516 }
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548 public int scanLiteral(int quote, XMLString content)
549 throws IOException {
550
551
552 if (fCurrentEntity.position == fCurrentEntity.count) {
553 load(0, true);
554 } else if (fCurrentEntity.position == fCurrentEntity.count - 1) {
555 fCurrentEntity.ch[0] = fCurrentEntity.ch[fCurrentEntity.count - 1];
556 load(1, false);
557 fCurrentEntity.position = 0;
558 }
559
560
561 int offset = fCurrentEntity.position;
562 int c = fCurrentEntity.ch[offset];
563 int newlines = 0;
564 boolean external = fCurrentEntity.isExternal();
565 if (c == '\n' || (c == '\r' && external)) {
566 do {
567 c = fCurrentEntity.ch[fCurrentEntity.position++];
568 if (c == '\r' && external) {
569 newlines++;
570 fCurrentEntity.lineNumber++;
571 fCurrentEntity.columnNumber = 1;
572 if (fCurrentEntity.position == fCurrentEntity.count) {
573 offset = 0;
574 fCurrentEntity.position = newlines;
575 if (load(newlines, false)) {
576 break;
577 }
578 }
579 if (fCurrentEntity.ch[fCurrentEntity.position] == '\n') {
580 fCurrentEntity.position++;
581 offset++;
582 }
583
584 else {
585 newlines++;
586 }
587
588 }
589 else if (c == '\n') {
590 newlines++;
591 fCurrentEntity.lineNumber++;
592 fCurrentEntity.columnNumber = 1;
593 if (fCurrentEntity.position == fCurrentEntity.count) {
594 offset = 0;
595 fCurrentEntity.position = newlines;
596 if (load(newlines, false)) {
597 break;
598 }
599 }
600
601
602
603
604
605
606
607 }
608 else {
609 fCurrentEntity.position--;
610 break;
611 }
612 } while (fCurrentEntity.position < fCurrentEntity.count - 1);
613 for (int i = offset; i < fCurrentEntity.position; i++) {
614 fCurrentEntity.ch[i] = '\n';
615 }
616 int length = fCurrentEntity.position - offset;
617 if (fCurrentEntity.position == fCurrentEntity.count - 1) {
618 content.setValues(fCurrentEntity.ch, offset, length);
619 return -1;
620 }
621 }
622
623
624 while (fCurrentEntity.position < fCurrentEntity.count) {
625 c = fCurrentEntity.ch[fCurrentEntity.position++];
626 if ((c == quote &&
627 (!fCurrentEntity.literal || external))
628 || c == '%' || !XMLChar.isContent(c)) {
629 fCurrentEntity.position--;
630 break;
631 }
632 }
633 int length = fCurrentEntity.position - offset;
634 fCurrentEntity.columnNumber += length - newlines;
635 content.setValues(fCurrentEntity.ch, offset, length);
636
637
638 if (fCurrentEntity.position != fCurrentEntity.count) {
639 c = fCurrentEntity.ch[fCurrentEntity.position];
640
641
642
643 if (c == quote && fCurrentEntity.literal) {
644 c = -1;
645 }
646 }
647 else {
648 c = -1;
649 }
650 return c;
651
652 }
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684 public boolean scanData(String delimiter, XMLStringBuffer buffer)
685 throws IOException {
686
687 boolean done = false;
688 int delimLen = delimiter.length();
689 char charAt0 = delimiter.charAt(0);
690 boolean external = fCurrentEntity.isExternal();
691 do {
692
693
694
695 if (fCurrentEntity.position == fCurrentEntity.count) {
696 load(0, true);
697 }
698 else if (fCurrentEntity.position >= fCurrentEntity.count - delimLen) {
699 System.arraycopy(fCurrentEntity.ch, fCurrentEntity.position,
700 fCurrentEntity.ch, 0, fCurrentEntity.count - fCurrentEntity.position);
701 load(fCurrentEntity.count - fCurrentEntity.position, false);
702 fCurrentEntity.position = 0;
703 }
704 if (fCurrentEntity.position >= fCurrentEntity.count - delimLen) {
705
706
707 int length = fCurrentEntity.count - fCurrentEntity.position;
708 buffer.append (fCurrentEntity.ch, fCurrentEntity.position,
709 length);
710 fCurrentEntity.columnNumber += fCurrentEntity.count;
711 fCurrentEntity.position = fCurrentEntity.count;
712 load(0,true);
713 return false;
714 }
715
716
717 int offset = fCurrentEntity.position;
718 int c = fCurrentEntity.ch[offset];
719 int newlines = 0;
720 if (c == '\n' || (c == '\r' && external)) {
721 do {
722 c = fCurrentEntity.ch[fCurrentEntity.position++];
723 if (c == '\r' && external) {
724 newlines++;
725 fCurrentEntity.lineNumber++;
726 fCurrentEntity.columnNumber = 1;
727 if (fCurrentEntity.position == fCurrentEntity.count) {
728 offset = 0;
729 fCurrentEntity.position = newlines;
730 if (load(newlines, false)) {
731 break;
732 }
733 }
734 if (fCurrentEntity.ch[fCurrentEntity.position] == '\n') {
735 fCurrentEntity.position++;
736 offset++;
737 }
738
739 else {
740 newlines++;
741 }
742 }
743 else if (c == '\n') {
744 newlines++;
745 fCurrentEntity.lineNumber++;
746 fCurrentEntity.columnNumber = 1;
747 if (fCurrentEntity.position == fCurrentEntity.count) {
748 offset = 0;
749 fCurrentEntity.position = newlines;
750 fCurrentEntity.count = newlines;
751 if (load(newlines, false)) {
752 break;
753 }
754 }
755 }
756 else {
757 fCurrentEntity.position--;
758 break;
759 }
760 } while (fCurrentEntity.position < fCurrentEntity.count - 1);
761 for (int i = offset; i < fCurrentEntity.position; i++) {
762 fCurrentEntity.ch[i] = '\n';
763 }
764 int length = fCurrentEntity.position - offset;
765 if (fCurrentEntity.position == fCurrentEntity.count - 1) {
766 buffer.append(fCurrentEntity.ch, offset, length);
767 return true;
768 }
769 }
770
771
772 OUTER: while (fCurrentEntity.position < fCurrentEntity.count) {
773 c = fCurrentEntity.ch[fCurrentEntity.position++];
774 if (c == charAt0) {
775
776 int delimOffset = fCurrentEntity.position - 1;
777 for (int i = 1; i < delimLen; i++) {
778 if (fCurrentEntity.position == fCurrentEntity.count) {
779 fCurrentEntity.position -= i;
780 break OUTER;
781 }
782 c = fCurrentEntity.ch[fCurrentEntity.position++];
783 if (delimiter.charAt(i) != c) {
784 fCurrentEntity.position--;
785 break;
786 }
787 }
788 if (fCurrentEntity.position == delimOffset + delimLen) {
789 done = true;
790 break;
791 }
792 }
793 else if (c == '\n' || (external && c == '\r')) {
794 fCurrentEntity.position--;
795 break;
796 }
797 else if (XMLChar.isInvalid(c)) {
798 fCurrentEntity.position--;
799 int length = fCurrentEntity.position - offset;
800 fCurrentEntity.columnNumber += length - newlines;
801 buffer.append(fCurrentEntity.ch, offset, length);
802 return true;
803 }
804 }
805 int length = fCurrentEntity.position - offset;
806 fCurrentEntity.columnNumber += length - newlines;
807 if (done) {
808 length -= delimLen;
809 }
810 buffer.append (fCurrentEntity.ch, offset, length);
811
812
813 } while (!done);
814 return !done;
815
816 }
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833 public boolean skipChar(int c) throws IOException {
834
835
836 if (fCurrentEntity.position == fCurrentEntity.count) {
837 load(0, true);
838 }
839
840
841 int cc = fCurrentEntity.ch[fCurrentEntity.position];
842 if (cc == c) {
843 fCurrentEntity.position++;
844 if (c == '\n') {
845 fCurrentEntity.lineNumber++;
846 fCurrentEntity.columnNumber = 1;
847 }
848 else {
849 fCurrentEntity.columnNumber++;
850 }
851 return true;
852 } else if (c == '\n' && cc == '\r' && fCurrentEntity.isExternal()) {
853
854 if (fCurrentEntity.position == fCurrentEntity.count) {
855 fCurrentEntity.ch[0] = (char)cc;
856 load(1, false);
857 }
858 fCurrentEntity.position++;
859 if (fCurrentEntity.ch[fCurrentEntity.position] == '\n') {
860 fCurrentEntity.position++;
861 }
862 fCurrentEntity.lineNumber++;
863 fCurrentEntity.columnNumber = 1;
864 return true;
865 }
866
867
868 return false;
869
870 }
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887 public boolean skipSpaces() throws IOException {
888
889
890 if (fCurrentEntity.position == fCurrentEntity.count) {
891 load(0, true);
892 }
893
894
895 int c = fCurrentEntity.ch[fCurrentEntity.position];
896 if (XMLChar.isSpace(c)) {
897 boolean external = fCurrentEntity.isExternal();
898 do {
899 boolean entityChanged = false;
900
901 if (c == '\n' || (external && c == '\r')) {
902 fCurrentEntity.lineNumber++;
903 fCurrentEntity.columnNumber = 1;
904 if (fCurrentEntity.position == fCurrentEntity.count - 1) {
905 fCurrentEntity.ch[0] = (char)c;
906 entityChanged = load(1, true);
907 if (!entityChanged)
908
909
910 fCurrentEntity.position = 0;
911 }
912 if (c == '\r' && external) {
913
914
915 if (fCurrentEntity.ch[++fCurrentEntity.position] != '\n') {
916 fCurrentEntity.position--;
917 }
918 }
919
920
921
922
923
924
925
926
927 }
928 else {
929 fCurrentEntity.columnNumber++;
930 }
931
932 if (!entityChanged)
933 fCurrentEntity.position++;
934 if (fCurrentEntity.position == fCurrentEntity.count) {
935 load(0, true);
936 }
937 } while (XMLChar.isSpace(c = fCurrentEntity.ch[fCurrentEntity.position]));
938 return true;
939 }
940
941
942 return false;
943
944 }
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959 public boolean skipString(String s) throws IOException {
960
961
962 if (fCurrentEntity.position == fCurrentEntity.count) {
963 load(0, true);
964 }
965
966
967 final int length = s.length();
968 for (int i = 0; i < length; i++) {
969 char c = fCurrentEntity.ch[fCurrentEntity.position++];
970 if (c != s.charAt(i)) {
971 fCurrentEntity.position -= i + 1;
972 return false;
973 }
974 if (i < length - 1 && fCurrentEntity.position == fCurrentEntity.count) {
975 System.arraycopy(fCurrentEntity.ch, fCurrentEntity.count - i - 1, fCurrentEntity.ch, 0, i + 1);
976
977
978 if (load(i + 1, false)) {
979 fCurrentEntity.position -= i + 1;
980 return false;
981 }
982 }
983 }
984 fCurrentEntity.columnNumber += length;
985 return true;
986
987 }
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005 final boolean load(int offset, boolean changeEntity)
1006 throws IOException {
1007
1008
1009 int length = fCurrentEntity.mayReadChunks?
1010 (fCurrentEntity.ch.length - offset):
1011 (DEFAULT_XMLDECL_BUFFER_SIZE);
1012 int count = fCurrentEntity.reader.read(fCurrentEntity.ch, offset,
1013 length);
1014
1015
1016 boolean entityChanged = false;
1017 if (count != -1) {
1018 if (count != 0) {
1019 fCurrentEntity.count = count + offset;
1020 fCurrentEntity.position = offset;
1021 }
1022 }
1023
1024
1025 else {
1026 fCurrentEntity.count = offset;
1027 fCurrentEntity.position = offset;
1028 entityChanged = true;
1029 if (changeEntity) {
1030 endEntity();
1031 if (fCurrentEntity == null) {
1032 throw new EOFException();
1033 }
1034
1035 if (fCurrentEntity.position == fCurrentEntity.count) {
1036 load(0, false);
1037 }
1038 }
1039 }
1040
1041 return entityChanged;
1042
1043 }
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067 private final class RewindableInputStream extends InputStream {
1068
1069 private InputStream fInputStream;
1070 private byte[] fData;
1071 private int fStartOffset;
1072 private int fEndOffset;
1073 private int fOffset;
1074 private int fLength;
1075 private int fMark;
1076
1077 public RewindableInputStream(InputStream is) {
1078 fData = new byte[DEFAULT_XMLDECL_BUFFER_SIZE];
1079 fInputStream = is;
1080 fStartOffset = 0;
1081 fEndOffset = -1;
1082 fOffset = 0;
1083 fLength = 0;
1084 fMark = 0;
1085 }
1086
1087 public void setStartOffset(int offset) {
1088 fStartOffset = offset;
1089 }
1090
1091 public void rewind() {
1092 fOffset = fStartOffset;
1093 }
1094
1095 public int read() throws IOException {
1096 int b = 0;
1097 if (fOffset < fLength) {
1098 return fData[fOffset++] & 0xff;
1099 }
1100 if (fOffset == fEndOffset) {
1101 return -1;
1102 }
1103 if (fOffset == fData.length) {
1104 byte[] newData = new byte[fOffset << 1];
1105 System.arraycopy(fData, 0, newData, 0, fOffset);
1106 fData = newData;
1107 }
1108 b = fInputStream.read();
1109 if (b == -1) {
1110 fEndOffset = fOffset;
1111 return -1;
1112 }
1113 fData[fLength++] = (byte)b;
1114 fOffset++;
1115 return b & 0xff;
1116 }
1117
1118 public int read(byte[] b, int off, int len) throws IOException {
1119 int bytesLeft = fLength - fOffset;
1120 if (bytesLeft == 0) {
1121 if (fOffset == fEndOffset) {
1122 return -1;
1123 }
1124
1125 if (fCurrentEntity.mayReadChunks) {
1126 return fInputStream.read(b, off, len);
1127 }
1128 int returnedVal = read();
1129 if (returnedVal == -1) {
1130 fEndOffset = fOffset;
1131 return -1;
1132 }
1133 b[off] = (byte)returnedVal;
1134 return 1;
1135 }
1136 if (len < bytesLeft) {
1137 if (len <= 0) {
1138 return 0;
1139 }
1140 }
1141 else {
1142 len = bytesLeft;
1143 }
1144 if (b != null) {
1145 System.arraycopy(fData, fOffset, b, off, len);
1146 }
1147 fOffset += len;
1148 return len;
1149 }
1150
1151 public long skip(long n)
1152 throws IOException
1153 {
1154 int bytesLeft;
1155 if (n <= 0) {
1156 return 0;
1157 }
1158 bytesLeft = fLength - fOffset;
1159 if (bytesLeft == 0) {
1160 if (fOffset == fEndOffset) {
1161 return 0;
1162 }
1163 return fInputStream.skip(n);
1164 }
1165 if (n <= bytesLeft) {
1166 fOffset += n;
1167 return n;
1168 }
1169 fOffset += bytesLeft;
1170 if (fOffset == fEndOffset) {
1171 return bytesLeft;
1172 }
1173 n -= bytesLeft;
1174
1175
1176
1177
1178
1179
1180
1181
1182 return fInputStream.skip(n) + bytesLeft;
1183 }
1184
1185 public int available() throws IOException {
1186 int bytesLeft = fLength - fOffset;
1187 if (bytesLeft == 0) {
1188 if (fOffset == fEndOffset) {
1189 return -1;
1190 }
1191 return fCurrentEntity.mayReadChunks ? fInputStream.available()
1192 : 0;
1193 }
1194 return bytesLeft;
1195 }
1196
1197 public void mark(int howMuch) {
1198 fMark = fOffset;
1199 }
1200
1201 public void reset() {
1202 fOffset = fMark;
1203 }
1204
1205 public boolean markSupported() {
1206 return true;
1207 }
1208
1209 public void close() throws IOException {
1210 if (fInputStream != null) {
1211 fInputStream.close();
1212 fInputStream = null;
1213 }
1214 }
1215 }
1216
1217
1218
1219 private void scanXMLDecl() throws IOException, JasperException {
1220
1221 if (skipString("<?xml")) {
1222 fMarkupDepth++;
1223
1224
1225 if (XMLChar.isName(peekChar())) {
1226 fStringBuffer.clear();
1227 fStringBuffer.append("xml");
1228 while (XMLChar.isName(peekChar())) {
1229 fStringBuffer.append((char)scanChar());
1230 }
1231 String target = fSymbolTable.addSymbol(fStringBuffer.ch,
1232 fStringBuffer.offset,
1233 fStringBuffer.length);
1234 scanPIData(target, fString);
1235 }
1236
1237
1238 else {
1239 scanXMLDeclOrTextDecl(false);
1240 }
1241 }
1242 }
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264 private void scanXMLDeclOrTextDecl(boolean scanningTextDecl)
1265 throws IOException, JasperException {
1266
1267
1268 scanXMLDeclOrTextDecl(scanningTextDecl, fStrings);
1269 fMarkupDepth--;
1270
1271
1272 String encodingPseudoAttr = fStrings[1];
1273
1274
1275 if (encodingPseudoAttr != null) {
1276 isEncodingSetInProlog = true;
1277 encoding = encodingPseudoAttr;
1278 }
1279 }
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307 private void scanXMLDeclOrTextDecl(boolean scanningTextDecl,
1308 String[] pseudoAttributeValues)
1309 throws IOException, JasperException {
1310
1311
1312 String version = null;
1313 String encoding = null;
1314 String standalone = null;
1315
1316
1317 final int STATE_VERSION = 0;
1318 final int STATE_ENCODING = 1;
1319 final int STATE_STANDALONE = 2;
1320 final int STATE_DONE = 3;
1321 int state = STATE_VERSION;
1322
1323 boolean dataFoundForTarget = false;
1324 boolean sawSpace = skipSpaces();
1325 while (peekChar() != '?') {
1326 dataFoundForTarget = true;
1327 String name = scanPseudoAttribute(scanningTextDecl, fString);
1328 switch (state) {
1329 case STATE_VERSION: {
1330 if (name == fVersionSymbol) {
1331 if (!sawSpace) {
1332 reportFatalError(scanningTextDecl
1333 ? "jsp.error.xml.spaceRequiredBeforeVersionInTextDecl"
1334 : "jsp.error.xml.spaceRequiredBeforeVersionInXMLDecl",
1335 null);
1336 }
1337 version = fString.toString();
1338 state = STATE_ENCODING;
1339 if (!version.equals("1.0")) {
1340
1341
1342
1343 err.jspError("jsp.error.xml.versionNotSupported",
1344 version);
1345 }
1346 } else if (name == fEncodingSymbol) {
1347 if (!scanningTextDecl) {
1348 err.jspError("jsp.error.xml.versionInfoRequired");
1349 }
1350 if (!sawSpace) {
1351 reportFatalError(scanningTextDecl
1352 ? "jsp.error.xml.spaceRequiredBeforeEncodingInTextDecl"
1353 : "jsp.error.xml.spaceRequiredBeforeEncodingInXMLDecl",
1354 null);
1355 }
1356 encoding = fString.toString();
1357 state = scanningTextDecl ? STATE_DONE : STATE_STANDALONE;
1358 } else {
1359 if (scanningTextDecl) {
1360 err.jspError("jsp.error.xml.encodingDeclRequired");
1361 }
1362 else {
1363 err.jspError("jsp.error.xml.versionInfoRequired");
1364 }
1365 }
1366 break;
1367 }
1368 case STATE_ENCODING: {
1369 if (name == fEncodingSymbol) {
1370 if (!sawSpace) {
1371 reportFatalError(scanningTextDecl
1372 ? "jsp.error.xml.spaceRequiredBeforeEncodingInTextDecl"
1373 : "jsp.error.xml.spaceRequiredBeforeEncodingInXMLDecl",
1374 null);
1375 }
1376 encoding = fString.toString();
1377 state = scanningTextDecl ? STATE_DONE : STATE_STANDALONE;
1378
1379
1380 } else if (!scanningTextDecl && name == fStandaloneSymbol) {
1381 if (!sawSpace) {
1382 err.jspError("jsp.error.xml.spaceRequiredBeforeStandalone");
1383 }
1384 standalone = fString.toString();
1385 state = STATE_DONE;
1386 if (!standalone.equals("yes") && !standalone.equals("no")) {
1387 err.jspError("jsp.error.xml.sdDeclInvalid");
1388 }
1389 } else {
1390 err.jspError("jsp.error.xml.encodingDeclRequired");
1391 }
1392 break;
1393 }
1394 case STATE_STANDALONE: {
1395 if (name == fStandaloneSymbol) {
1396 if (!sawSpace) {
1397 err.jspError("jsp.error.xml.spaceRequiredBeforeStandalone");
1398 }
1399 standalone = fString.toString();
1400 state = STATE_DONE;
1401 if (!standalone.equals("yes") && !standalone.equals("no")) {
1402 err.jspError("jsp.error.xml.sdDeclInvalid");
1403 }
1404 } else {
1405 err.jspError("jsp.error.xml.encodingDeclRequired");
1406 }
1407 break;
1408 }
1409 default: {
1410 err.jspError("jsp.error.xml.noMorePseudoAttributes");
1411 }
1412 }
1413 sawSpace = skipSpaces();
1414 }
1415
1416 if (scanningTextDecl && state != STATE_DONE) {
1417 err.jspError("jsp.error.xml.morePseudoAttributes");
1418 }
1419
1420
1421
1422 if (scanningTextDecl) {
1423 if (!dataFoundForTarget && encoding == null) {
1424 err.jspError("jsp.error.xml.encodingDeclRequired");
1425 }
1426 } else {
1427 if (!dataFoundForTarget && version == null) {
1428 err.jspError("jsp.error.xml.versionInfoRequired");
1429 }
1430 }
1431
1432
1433 if (!skipChar('?')) {
1434 err.jspError("jsp.error.xml.xmlDeclUnterminated");
1435 }
1436 if (!skipChar('>')) {
1437 err.jspError("jsp.error.xml.xmlDeclUnterminated");
1438
1439 }
1440
1441
1442 pseudoAttributeValues[0] = version;
1443 pseudoAttributeValues[1] = encoding;
1444 pseudoAttributeValues[2] = standalone;
1445 }
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464 public String scanPseudoAttribute(boolean scanningTextDecl,
1465 XMLString value)
1466 throws IOException, JasperException {
1467
1468 String name = scanName();
1469 if (name == null) {
1470 err.jspError("jsp.error.xml.pseudoAttrNameExpected");
1471 }
1472 skipSpaces();
1473 if (!skipChar('=')) {
1474 reportFatalError(scanningTextDecl ?
1475 "jsp.error.xml.eqRequiredInTextDecl"
1476 : "jsp.error.xml.eqRequiredInXMLDecl",
1477 name);
1478 }
1479 skipSpaces();
1480 int quote = peekChar();
1481 if (quote != '\'' && quote != '"') {
1482 reportFatalError(scanningTextDecl ?
1483 "jsp.error.xml.quoteRequiredInTextDecl"
1484 : "jsp.error.xml.quoteRequiredInXMLDecl" ,
1485 name);
1486 }
1487 scanChar();
1488 int c = scanLiteral(quote, value);
1489 if (c != quote) {
1490 fStringBuffer2.clear();
1491 do {
1492 fStringBuffer2.append(value);
1493 if (c != -1) {
1494 if (c == '&' || c == '%' || c == '<' || c == ']') {
1495 fStringBuffer2.append((char)scanChar());
1496 }
1497 else if (XMLChar.isHighSurrogate(c)) {
1498 scanSurrogates(fStringBuffer2);
1499 }
1500 else if (XMLChar.isInvalid(c)) {
1501 String key = scanningTextDecl
1502 ? "jsp.error.xml.invalidCharInTextDecl"
1503 : "jsp.error.xml.invalidCharInXMLDecl";
1504 reportFatalError(key, Integer.toString(c, 16));
1505 scanChar();
1506 }
1507 }
1508 c = scanLiteral(quote, value);
1509 } while (c != quote);
1510 fStringBuffer2.append(value);
1511 value.setValues(fStringBuffer2);
1512 }
1513 if (!skipChar(quote)) {
1514 reportFatalError(scanningTextDecl ?
1515 "jsp.error.xml.closeQuoteMissingInTextDecl"
1516 : "jsp.error.xml.closeQuoteMissingInXMLDecl",
1517 name);
1518 }
1519
1520
1521 return name;
1522
1523 }
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538 private void scanPIData(String target, XMLString data)
1539 throws IOException, JasperException {
1540
1541
1542 if (target.length() == 3) {
1543 char c0 = Character.toLowerCase(target.charAt(0));
1544 char c1 = Character.toLowerCase(target.charAt(1));
1545 char c2 = Character.toLowerCase(target.charAt(2));
1546 if (c0 == 'x' && c1 == 'm' && c2 == 'l') {
1547 err.jspError("jsp.error.xml.reservedPITarget");
1548 }
1549 }
1550
1551
1552 if (!skipSpaces()) {
1553 if (skipString("?>")) {
1554
1555 data.clear();
1556 return;
1557 }
1558 else {
1559
1560 err.jspError("jsp.error.xml.spaceRequiredInPI");
1561 }
1562 }
1563
1564 fStringBuffer.clear();
1565
1566 if (scanData("?>", fStringBuffer)) {
1567 do {
1568 int c = peekChar();
1569 if (c != -1) {
1570 if (XMLChar.isHighSurrogate(c)) {
1571 scanSurrogates(fStringBuffer);
1572 } else if (XMLChar.isInvalid(c)) {
1573 err.jspError("jsp.error.xml.invalidCharInPI",
1574 Integer.toHexString(c));
1575 scanChar();
1576 }
1577 }
1578 } while (scanData("?>", fStringBuffer));
1579 }
1580 data.setValues(fStringBuffer);
1581
1582 }
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595 private boolean scanSurrogates(XMLStringBuffer buf)
1596 throws IOException, JasperException {
1597
1598 int high = scanChar();
1599 int low = peekChar();
1600 if (!XMLChar.isLowSurrogate(low)) {
1601 err.jspError("jsp.error.xml.invalidCharInContent",
1602 Integer.toString(high, 16));
1603 return false;
1604 }
1605 scanChar();
1606
1607
1608 int c = XMLChar.supplemental((char)high, (char)low);
1609
1610
1611 if (!XMLChar.isValid(c)) {
1612 err.jspError("jsp.error.xml.invalidCharInContent",
1613 Integer.toString(c, 16));
1614 return false;
1615 }
1616
1617
1618 buf.append((char)high);
1619 buf.append((char)low);
1620
1621 return true;
1622
1623 }
1624
1625
1626
1627
1628
1629
1630 private void reportFatalError(String msgId, String arg)
1631 throws JasperException {
1632 err.jspError(msgId, arg);
1633 }
1634
1635 }
1636
1637