|
32 | 32 | import java.util.regex.Matcher; |
33 | 33 | import java.text.MessageFormat; |
34 | 34 |
|
| 35 | +import org.apache.commons.io.ByteOrderMark; |
| 36 | + |
35 | 37 | /** |
36 | 38 | * Character stream that handles all the necessary Voodo to figure out the |
37 | 39 | * charset encoding of the XML document within the stream. |
@@ -75,6 +77,12 @@ public class XmlStreamReader extends Reader { |
75 | 77 |
|
76 | 78 | private static final String EBCDIC = "CP1047"; |
77 | 79 |
|
| 80 | + private static final ByteOrderMark XML_UTF_8 = new ByteOrderMark(UTF_8, 0x3C, 0x3F, 0x78, 0x6D); |
| 81 | + private static final ByteOrderMark XML_UTF_16BE = new ByteOrderMark(UTF_16BE, 0x00, 0x3C, 0x00, 0x3F); |
| 82 | + private static final ByteOrderMark XML_UTF_16LE = new ByteOrderMark(UTF_16LE, 0x3C, 0x00, 0x3F, 0x00); |
| 83 | + private static final ByteOrderMark XML_EBCDIC = new ByteOrderMark(EBCDIC, 0x4C, 0x6F, 0xA7, 0x94); |
| 84 | + |
| 85 | + |
78 | 86 | private static String staticDefaultEncoding = null; |
79 | 87 |
|
80 | 88 | private Reader reader; |
@@ -406,27 +414,41 @@ public void close() throws IOException { |
406 | 414 |
|
407 | 415 | private void doRawStream(InputStream is, boolean lenient) |
408 | 416 | throws IOException { |
409 | | - BufferedInputStream pis = new BufferedInputStream(is, BUFFER_SIZE); |
410 | | - String bomEnc = getBOMEncoding(pis); |
411 | | - String xmlGuessEnc = getXMLGuessEncoding(pis); |
| 417 | + BOMInputStream bom = createBomStream(new BufferedInputStream(is, BUFFER_SIZE)); |
| 418 | + BOMInputStream pis = createXmlStream(bom); |
| 419 | + String bomEnc = (bom.hasBOM() ? bom.getBOM().getCharsetName() : null); |
| 420 | + String xmlGuessEnc = (pis.hasBOM() ? pis.getBOM().getCharsetName() : null); |
412 | 421 | String xmlEnc = getXmlProlog(pis, xmlGuessEnc); |
413 | 422 | String encoding = calculateRawEncoding(bomEnc, xmlGuessEnc, xmlEnc, pis); |
414 | 423 | prepareReader(pis, encoding); |
415 | 424 | } |
416 | 425 |
|
417 | 426 | private void doHttpStream(InputStream is, String httpContentType, |
418 | 427 | boolean lenient) throws IOException { |
419 | | - BufferedInputStream pis = new BufferedInputStream(is, BUFFER_SIZE); |
| 428 | + BOMInputStream bom = createBomStream(new BufferedInputStream(is, BUFFER_SIZE)); |
| 429 | + BOMInputStream pis = createXmlStream(bom); |
420 | 430 | String cTMime = getContentTypeMime(httpContentType); |
421 | 431 | String cTEnc = getContentTypeEncoding(httpContentType); |
422 | | - String bomEnc = getBOMEncoding(pis); |
423 | | - String xmlGuessEnc = getXMLGuessEncoding(pis); |
| 432 | + String bomEnc = (bom.hasBOM() ? bom.getBOM().getCharsetName() : null); |
| 433 | + String xmlGuessEnc = (pis.hasBOM() ? pis.getBOM().getCharsetName() : null); |
424 | 434 | String xmlEnc = getXmlProlog(pis, xmlGuessEnc); |
425 | 435 | String encoding = calculateHttpEncoding(cTMime, cTEnc, bomEnc, |
426 | 436 | xmlGuessEnc, xmlEnc, pis, lenient); |
427 | 437 | prepareReader(pis, encoding); |
428 | 438 | } |
429 | 439 |
|
| 440 | + private BOMInputStream createBomStream(InputStream delegate) { |
| 441 | + BOMInputStream bis = |
| 442 | + new BOMInputStream(delegate, false, ByteOrderMark.UTF_8, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_16LE); |
| 443 | + return bis; |
| 444 | + } |
| 445 | + |
| 446 | + private BOMInputStream createXmlStream(InputStream delegate) { |
| 447 | + BOMInputStream bis = |
| 448 | + new BOMInputStream(delegate, true, XML_UTF_8, XML_UTF_16BE, XML_UTF_16LE, XML_EBCDIC); |
| 449 | + return bis; |
| 450 | + } |
| 451 | + |
430 | 452 | private void prepareReader(InputStream is, String encoding) |
431 | 453 | throws IOException { |
432 | 454 | reader = new InputStreamReader(is, encoding); |
@@ -556,70 +578,12 @@ private static String getContentTypeEncoding(String httpContentType) { |
556 | 578 | return encoding; |
557 | 579 | } |
558 | 580 |
|
559 | | - // returns the BOM in the stream, NULL if not present, |
560 | | - // if there was BOM the in the stream it is consumed |
561 | | - private static String getBOMEncoding(BufferedInputStream is) |
562 | | - throws IOException { |
563 | | - String encoding = null; |
564 | | - int[] bytes = new int[3]; |
565 | | - is.mark(3); |
566 | | - bytes[0] = is.read(); |
567 | | - bytes[1] = is.read(); |
568 | | - bytes[2] = is.read(); |
569 | | - |
570 | | - if (bytes[0] == 0xFE && bytes[1] == 0xFF) { |
571 | | - encoding = UTF_16BE; |
572 | | - is.reset(); |
573 | | - is.read(); |
574 | | - is.read(); |
575 | | - } else if (bytes[0] == 0xFF && bytes[1] == 0xFE) { |
576 | | - encoding = UTF_16LE; |
577 | | - is.reset(); |
578 | | - is.read(); |
579 | | - is.read(); |
580 | | - } else if (bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) { |
581 | | - encoding = UTF_8; |
582 | | - } else { |
583 | | - is.reset(); |
584 | | - } |
585 | | - return encoding; |
586 | | - } |
587 | | - |
588 | | - // returns the best guess for the encoding by looking the first bytes of the |
589 | | - // stream, '<?' |
590 | | - private static String getXMLGuessEncoding(BufferedInputStream is) |
591 | | - throws IOException { |
592 | | - String encoding = null; |
593 | | - int[] bytes = new int[4]; |
594 | | - is.mark(4); |
595 | | - bytes[0] = is.read(); |
596 | | - bytes[1] = is.read(); |
597 | | - bytes[2] = is.read(); |
598 | | - bytes[3] = is.read(); |
599 | | - is.reset(); |
600 | | - |
601 | | - if (bytes[0] == 0x00 && bytes[1] == 0x3C && bytes[2] == 0x00 |
602 | | - && bytes[3] == 0x3F) { |
603 | | - encoding = UTF_16BE; |
604 | | - } else if (bytes[0] == 0x3C && bytes[1] == 0x00 && bytes[2] == 0x3F |
605 | | - && bytes[3] == 0x00) { |
606 | | - encoding = UTF_16LE; |
607 | | - } else if (bytes[0] == 0x3C && bytes[1] == 0x3F && bytes[2] == 0x78 |
608 | | - && bytes[3] == 0x6D) { |
609 | | - encoding = UTF_8; |
610 | | - } else if (bytes[0] == 0x4C && bytes[1] == 0x6F && bytes[2] == 0xA7 |
611 | | - && bytes[3] == 0x94) { |
612 | | - encoding = EBCDIC; |
613 | | - } |
614 | | - return encoding; |
615 | | - } |
616 | | - |
617 | 581 | public static final Pattern ENCODING_PATTERN = Pattern.compile( |
618 | 582 | "<\\?xml.*encoding[\\s]*=[\\s]*((?:\".[^\"]*\")|(?:'.[^']*'))", |
619 | 583 | Pattern.MULTILINE); |
620 | 584 |
|
621 | 585 | // returns the encoding declared in the <?xml encoding=...?>, NULL if none |
622 | | - private static String getXmlProlog(BufferedInputStream is, String guessedEnc) |
| 586 | + private static String getXmlProlog(InputStream is, String guessedEnc) |
623 | 587 | throws IOException { |
624 | 588 | String encoding = null; |
625 | 589 | if (guessedEnc != null) { |
|
0 commit comments