home *** CD-ROM | disk | FTP | other *** search
/ Dynamic HTML in Action / Dynamicke-HTML-v-akci-covermount.bin / XML / PARSER / XMLINST.EXE / classes / com / ms / xml / util / XMLInputStream.java < prev    next >
Encoding:
Java Source  |  1997-12-01  |  18.2 KB  |  569 lines

  1. /*
  2.  * @(#)XMLInputStream.java 1.0 6/10/97
  3.  * 
  4.  * Copyright (c) 1997 Microsoft, Corp. All Rights Reserved.
  5.  * 
  6.  */
  7. package com.ms.xml.util;
  8.  
  9. import java.io.*;
  10. import java.net.*;
  11.  
  12. /**
  13.  * 
  14.  * A Reader specifically for dealing with different encoding formats 
  15.  * as well as liitleendian files.
  16.  *
  17.  * @version 1.0, 6/10/97
  18.  */
  19. public class XMLInputStream extends InputStream
  20. {
  21.     /**
  22.      * The state enumerators
  23.      */    
  24.     static final int INPUTSR     = 1;   // The different states that concern the read() 
  25.     static final int UCS2        = 2;   // The POP states imply that there are characters
  26.     static final int ASCII       = 3;   // stored in the next[] stack.  There are separate
  27.     static final int INPUTSR_POP = 4;   // states for POP in order to speed up the read().
  28.     static final int UCS2_POP    = 5;   // There is still room for future additions, such
  29.     static final int ASCII_POP   = 6;   // as a UCS-4 state.  
  30.                                         // NOTE:  All encodings that can use InputStreamReader
  31.                                         //        fall into the INPUTSR state
  32.     /**
  33.      * input buffer size on windows platforms
  34.      */ 
  35.     static final int SIZE = 1024;       // input buffer size
  36.  
  37.     /**
  38.      * encoding numbers
  39.      */
  40.     static final int INTUTF8   = 0;
  41.     static final int INTASCII  = 1;
  42.     static final int INTUCS2   = 2;
  43.     static final int INTUCS4   = 3;
  44.     static final int INTEBCDIC = 4;
  45.     static final int INT1252 = 5;
  46.  
  47.  
  48.     /**
  49.      * Builds the XMLInputStream.
  50.      * This constructor is intended to be called on Windows to speed up input
  51.      * speed. The decoding is done by IXMLStream
  52.      * 
  53.      * Note:
  54.      * This constructor relies on CXMLStream class in xmlurlstream.dll. If 
  55.      * xmlurlstream.dll or xmlurlstream.tlb is not properly registered on the 
  56.      * system, or the encoding of the input stream cannot be handled,
  57.      * this constructor throws an IOException
  58.      */
  59.     public XMLInputStream(URL url) throws IOException
  60.     {
  61.         try 
  62.         {
  63.             Class clazz = Class.forName("com.ms.xml.xmlstream.XMLStream");
  64.             xmlis = (XMLStreamReader)clazz.newInstance();
  65.             intEncoding = xmlis.open(url.toString());
  66.         }
  67.         catch (Throwable e) 
  68.         {
  69.             throw new IOException("Error opening input stream for \"" + 
  70.                     url.toString() + "\": " + e.toString());
  71.         }
  72.  
  73.         onWindows = true;
  74.         switch (intEncoding)
  75.         {
  76.             case INTUTF8:    encoding = "UTF-8";  break;
  77.             case INTASCII:   encoding = "ASCII";  break;
  78.             case INTUCS2:    encoding = "UCS-2";  break;
  79.             case INTUCS4:    encoding = "UCS-4";  break;
  80.             case INTEBCDIC:  encoding = "EBCDIC"; break;
  81.             case INT1252:   encoding = "windows-1252"; break;
  82.             default:
  83.                 throw new IOException("Error opening input stream for \"" + 
  84.                     url.toString() + "\"");
  85.         }
  86.     }
  87.  
  88.  
  89.     /**
  90.      * Builds the XMLInputStream.
  91.      * Reads the first four bytes of the InputStream in order to make a guess
  92.      * as to the character encoding of the file.
  93.      * Assumes that the document is following the XML standard and that
  94.      * any non-UTF-8 file will begin with a <?XML> tag.
  95.      */
  96.     public XMLInputStream(InputStream in)
  97.     {
  98.         String version = System.getProperty("java.version");
  99.         jdk11 = version.equals("1.1") ? true : false;
  100.  
  101.         littleendian   = false;
  102.         caseInsensitive = false;
  103.  
  104.         byteOrderMark  = false;
  105.         encoding       = "UTF-8";    // Default encoding
  106.  
  107.         this.in        = in;
  108.         this.insr      = null;
  109.  
  110.         readState      = ASCII;
  111.  
  112.         boolean setDefault = false;
  113.  
  114.         try
  115.         {
  116.             char c1, c2, c3, c4;
  117.  
  118.             c1 = (char)in.read();
  119.             c2 = (char)in.read();
  120.             c3 = (char)in.read();
  121.             c4 = (char)in.read();
  122.             if( c1 == 0xFE && c2 == 0xFF && c3 == 0x00 && c4 == 0x3C )
  123.             {
  124.                 // UCS-2, big-endian
  125.                 littleendian  = false;
  126.                 byteOrderMark = true;
  127.                 readState     = UCS2_POP;
  128.                 encoding      = "UCS-2";
  129.             }
  130.             else if( c1 == 0xFF && c2 == 0xFE && c3 == 0x3C && c4 == 0x00 )
  131.             {
  132.                 // UCS-2, little-endian
  133.                 littleendian  = true;
  134.                 byteOrderMark = true;
  135.                 readState     = UCS2_POP;
  136.                 encoding      = "UCS-2";
  137.  
  138.                 this.in       = new ByteSwapInputStream( in );
  139.             }
  140.             else if( c1 == 0x00 && c2 == 0x3C && c3 == 0x00 && c4 == 0x3F )
  141.             {
  142.                 // UCS-2, big-endian, no Byte Order Mark
  143.                 littleendian = false;
  144.                 readState    = UCS2_POP;
  145.                 encoding     = "UCS-2";
  146.             }
  147.             else if( c1 == 0x3C && c2 == 0x00 && c3 == 0x3F && c4 == 0x00 )
  148.             {
  149.                 // UCS-2, little-endian, no Byte Order Mark
  150.                 littleendian = true;
  151.                 readState    = UCS2_POP;
  152.                 encoding     = "UCS-2";
  153.  
  154.                 this.in      = new ByteSwapInputStream( in );             
  155.             }
  156.             else if( c1 == 0x3C && c2 == 0x3F && 
  157.                 Character.toUpperCase(c3) == 0x58 && 
  158.                 Character.toUpperCase(c4) == 0x4D )
  159.             {
  160.                 // UTF-8, ISO 646, ASCII, some part of ISO 8859, Shift-JIS, EUC,
  161.                 // or any other encoding that ensures that ASCII has normal positions
  162.                 readState = ASCII_POP;
  163.                 encoding  = "ASCII";
  164.             }
  165.             else if( c1 == 0x00 && c2 == 0x00 && c3 == 0x00 && c4 == 0x3C )
  166.             {
  167.                 // UCS-4, big-endian machine (1234 order)
  168.                 readState = ASCII_POP;  // Until UCS-4 is implemented
  169.                 encoding  = "UCS-4";
  170.             }
  171.             else if( c1 == 0x3C && c2 == 0x00 && c3 == 0x00 && c4 == 0x00 )
  172.             {
  173.                 // UCS-4, little-endian machine (4321 order)
  174.                 readState = ASCII_POP;  // Until UCS-4 is implemented
  175.                 encoding  = "UCS-4";
  176.             }
  177.             else if( c1 == 0x00 && c2 == 0x00 && c3 == 0x3C && c4 == 0x00 )
  178.             {
  179.                 // UCS-4, unusual octet order (2143 order)
  180.                 readState = ASCII_POP;  // Until UCS-4 is implemented
  181.                 encoding  = "UCS-4";
  182.             }
  183.             else if( c1 == 0x00 && c2 == 0x3C && c3 == 0x00 && c4 == 0x00 )
  184.             {
  185.                 // UCS-4, unusual octet order (3412 order)
  186.                 readState = ASCII_POP;  // Until UCS-4 is implemented
  187.                 encoding  = "UCS-4";
  188.             }
  189.             else if( c1 == 0x4C && c2 == 0x6F && c3 == 0xE7 && c4 == 0xD4 )
  190.             {
  191.                 // EBCDIC - We do NOT support this!
  192.                 readState = ASCII_POP;  // Until EBCDIC is implemented
  193.                 encoding  = "EBCDIC";
  194.             }
  195.             else
  196.             {
  197.                 // UTF-8 without an <?XML> tag (assuming data is not corrupt)
  198.                 setDefault = true;
  199.             }
  200.  
  201.             if( !encoding.equals( "UCS-2" ) )
  202.             {
  203.                 push(c4);
  204.                 push(c3);
  205.                 push(c2);
  206.                 push(c1);
  207.             }
  208.             else
  209.             {
  210.                 if( littleendian )
  211.                 {
  212.                     push(c3);
  213.                     push(c4);
  214.                     if( !byteOrderMark )
  215.                     {
  216.                         push(c1);
  217.                         push(c2);        
  218.                     }
  219.                 }
  220.                 else
  221.                 {
  222.                     push(c4);
  223.                     push(c3);
  224.                     if( !byteOrderMark )
  225.                     {
  226.                         push(c2);
  227.                         push(c1);        
  228.                     }
  229.                 }
  230.             }
  231.         }
  232.         catch (IOException e) 
  233.         {
  234.             // Can't do lookahead, so use default UTF-8
  235.             setDefault = true;
  236.         } 
  237.  
  238.         pos = -1;
  239.  
  240.         if( setDefault )
  241.         {
  242.             try 
  243.             {  
  244.                 if (! jdk11)
  245.                     throw new IOException("Readers not supported in JDK 1.0");
  246.                 // guess that the <?xml encoding=...?> tag will be read in
  247.                 // less that 4096 bytes.
  248.                 if (! in.markSupported()) 
  249.                 {
  250.                     in = new BufferedInputStream(in);
  251.                 }
  252.                 in.mark(4096);
  253.                 insr      = new InputStreamReader( in, "UTF8" );
  254.                 readState = INPUTSR_POP;                    
  255.                 encoding  = "UTF-8";
  256.             } 
  257.             catch( IOException e2 ) 
  258.             {
  259.                 // If there is an exception we can
  260.                 // just continue and treat file like ASCII text.
  261.                 readState = ASCII_POP;   
  262.                 encoding = "ASCII";
  263.             }          
  264.         }
  265.     }
  266.  
  267.     private void push(char next)
  268.     {
  269.         if (index == 3) 
  270.         {
  271.             System.exit(0);
  272.         }
  273.         this.next[++index] = next;
  274.     }
  275.  
  276.     /**
  277.      * Returns the next unicode char in the stream.  The read done 
  278.      * depends on the current readState.  POP states imply that there
  279.      * are characters that have been pushed onto the next[] stack.
  280.      */
  281.     public int read() throws IOException
  282.     {
  283.         // On windows 
  284.         if (onWindows)
  285.         {
  286.             int len = 0;
  287.             if (index >= size)
  288.             {
  289.                 if (eof)
  290.                     return -1;
  291.                 try 
  292.                 {
  293.                     len = xmlis.read(buffer, SIZE);
  294.                 }
  295.                 catch (Exception e) 
  296.                 {
  297. //                    System.out.println("Unexpected error: " + e.toString());
  298.                     return -1;
  299.                 }
  300.                 
  301.                 if (len <= 0)
  302.                     return -1;
  303.                 if (len < SIZE)
  304.                     eof = true;
  305.                 size = len;
  306.                 index = 0;
  307.             }
  308.             int rc = buffer[index++];
  309.             if (rc == 0)
  310.             {
  311.                 String err = "Stream error: unexpected null";
  312.                 throw new IOException(err);
  313.             }
  314.             return rc;
  315.         }
  316.  
  317.         // On other platform
  318.         switch( readState )
  319.         {
  320.             case INPUTSR:
  321.                 pos++; 
  322.                 return insr.read();
  323.             case ASCII:
  324.                 return in.read();
  325.             case UCS2:            
  326.                 {
  327.                     int b1, b2;
  328.  
  329.                     b1 = in.read();
  330.     
  331.                     if( b1 == -1 )
  332.                         return -1;
  333.         
  334.                     b2 = in.read();
  335.  
  336.                     return ((b1 << 8) | b2);                         
  337.                 }              
  338.             case INPUTSR_POP:
  339.                 if (index >= 0) 
  340.                 {
  341.                     return next[index--];
  342.                 }
  343.                 else 
  344.                 {
  345.                     readState = INPUTSR;   
  346.                     return read();
  347.                 }
  348.             case UCS2_POP:        
  349.                 {
  350.                     int b1, b2;
  351.  
  352.                     if (index >= 0) 
  353.                     {
  354.                         b1 = next[index--];
  355.                     } 
  356.                     else 
  357.                     {
  358.                         readState = UCS2;
  359.                         b1 = in.read();
  360.                     }
  361.                 
  362.                     if( b1 == -1 )
  363.                         return -1;
  364.     
  365.                     if (index >= 0) 
  366.                     {
  367.                         b2 = next[index--];
  368.                     } 
  369.                     else 
  370.                     {
  371.                         readState = UCS2;
  372.                         b2 = in.read();
  373.                     }
  374.  
  375.                     return ((b1 << 8) | b2);              
  376.                 }        
  377.             case ASCII_POP:
  378.             default:
  379.                 if (index >= 0) 
  380.                 {
  381.                     return next[index--];
  382.                 } 
  383.                 else 
  384.                 {
  385.                     readState = ASCII;
  386.                     return in.read();
  387.                 }
  388.         }
  389.     }
  390.     
  391.  
  392.     /**
  393.      * Defines the character encoding of the stream.  The new character encoding
  394.      * must agree with the encoding determined by the constructer.  setEncoding
  395.      * is used to clarify between encodings that are not fully determinable 
  396.      * through the first four bytes in a stream and not to change the encoding.
  397.      * This method must be called within 4096 reads() after construction.
  398.      */
  399.     public void setEncoding( String encoding ) throws IOException
  400.     {
  401.         insr = null;
  402.         String encvm; // Java VM's version of encoding.
  403.         int newEncoding = 0;
  404.  
  405.         if( encoding.equalsIgnoreCase( "ISO-10646-UCS-2" ) ||
  406.             encoding.equalsIgnoreCase( "UCS-2" ) )
  407.         {         
  408.             if( !this.encoding.equalsIgnoreCase( "UCS-2" ) )
  409.                 throw new IOException( "Illegal Change of Encoding" );
  410.  
  411.             readState = UCS2;
  412.             this.encoding = "UCS-2";
  413.             return;
  414.         }
  415.         else if( encoding.equalsIgnoreCase( "Shift_JIS" ) )
  416.         {
  417.             if (onWindows)
  418.                 throw new IOException( "SJIS not yet supported" );
  419.             encvm = "SJIS";
  420.         }
  421.         else if( encoding.equalsIgnoreCase( "ISO-8859-1" ) )
  422.         {
  423.             if (onWindows)
  424.                 throw new IOException( "8859_1 not yet supported" );
  425.             encvm = "8859_1";
  426.         }
  427.         else if( encoding.equalsIgnoreCase( "ISO-10646-UCS-4" ) )
  428.         {  // UCS-4 NOT YET SUPPORTED!
  429.             throw new IOException( "UCS-4 not yet supported" );
  430.         }
  431.         else if( encoding.equalsIgnoreCase( "UTF-8" ) )
  432.         {  
  433.             encvm = "UTF8";
  434.             newEncoding = INTUTF8; 
  435.         }
  436.         else
  437.         {
  438.             if (onWindows)
  439.             {
  440.                 if (encoding.equals("windows-1252"))
  441.                     newEncoding = INT1252 ;
  442.                 else
  443.                     throw new IOException( encoding + " not yet supported" );
  444.             }
  445.             encvm = encoding; // try passing through to VM...
  446.         }
  447.  
  448.         if( !this.encoding.equalsIgnoreCase( "ASCII" ) &&
  449.             !this.encoding.equalsIgnoreCase( "UTF-8" ) )
  450.             throw new IOException( "Illegal Change of Encoding" );
  451.  
  452.         if (onWindows)
  453.         {
  454.             if (intEncoding != newEncoding)
  455.             {
  456.                 xmlis.setEncoding(newEncoding, index); 
  457.                 index = size = 0;
  458.                 eof = false; // since we have more to read now.
  459.             }
  460.             return;
  461.         }
  462.             
  463.         if (this.encoding.equalsIgnoreCase( "ASCII" )) 
  464.         {
  465.             insr = null;
  466.             readState = ASCII_POP; 
  467.         } 
  468.         else 
  469.         {
  470.             if (jdk11) {
  471.                 if (pos != -1) 
  472.                 {
  473.                     in.reset();     // This fixes a nasty bug in that InputStreamReaders
  474.                     in.skip(pos+1);   // now buffer their input.
  475.                 }
  476.                 insr = new InputStreamReader( in, encvm );
  477.                 readState = INPUTSR;                
  478.                 this.encoding = encoding;
  479.             } 
  480.             else 
  481.             {
  482.                throw new IOException( encvm + " is not supported by your Java Virtual Machine." +
  483.                     "  Try installing the latest VM from http://www.microsoft.com/java/download.htm");           
  484.             }
  485.         }
  486.     }
  487.  
  488.     
  489.     /**
  490.      * Creates a new XMLOutputStream with the proper initial state.
  491.      * XMLOutputStreams should always be created through this method
  492.      * if the output stream is to mimic this input stream.
  493.      */
  494.     public XMLOutputStream createOutputStream( OutputStream out)
  495.     {
  496.         XMLOutputStream xmlOut = new XMLOutputStream( out );
  497.         try {
  498.             xmlOut.setEncoding( encoding, 
  499.                             littleendian, 
  500.                             byteOrderMark );
  501.         } catch (IOException e ) {
  502.             // Hmm.  This should never happen because we already
  503.             // successfully created the input stream.
  504.         }
  505.         return xmlOut;
  506.     }
  507.  
  508.     /**
  509.      * Close the stream and release system resources.
  510.      */
  511.     public void close() throws IOException
  512.     {
  513.         if (xmlis != null)
  514.             xmlis.close();
  515.         else if (insr != null)
  516.             insr.close();
  517.         else if (in != null)
  518.             in.close();
  519.     }
  520.  
  521.     /**
  522.      * Character pushed back into the stream.
  523.      * Need to be able push back four characters for auto-encoding
  524.      * detection support.
  525.      */
  526.     private int next[] = new int[4];
  527.     private int index = -1;
  528.  
  529.     /**
  530.      * The stream readers
  531.      */
  532.     private InputStream       in;
  533.     private InputStreamReader insr;
  534.  
  535.     /**
  536.      * We remember the current position in the input stream so that
  537.      * we can re-scan input after doing a reset() when setEncoding 
  538.      * is called.  We have to do a mark()/reset() on setEncoding because
  539.      * the first UTF8 InputStreamReader may buffer the input !
  540.      */
  541.     private int pos = 0; 
  542.  
  543.     /**
  544.      * Character encoding state
  545.      */
  546.     private String  encoding;        
  547.     private boolean littleendian;    // file littleendian (only applies to UCS-2 encoded files)
  548.     private boolean byteOrderMark;   // byteOrderMark at the beginning of file (UCS-2)
  549.     private int     readState;
  550.     private boolean jdk11;
  551.  
  552.     /**
  553.      * Instance varibales for Windows platforms
  554.      */
  555.     private XMLStreamReader xmlis  = null;    // input stream pointer
  556.     private int intEncoding   = -1;           // encoding of input stream
  557.     private int[] buffer = new int[SIZE];     // input buffer
  558.     private int size  = -1;                   // bytes in buffer 
  559.     private boolean eof = false;              // whether the end of input stream has been reached
  560.     private boolean onWindows = false;        // whether this program is running on a windows machine
  561.  
  562.     /**
  563.      * Whether the document to be parsed caseInsensitive.
  564.      * (if so, all names are folded to upper case).
  565.      * Default is 'false'.
  566.      */ 
  567.     public boolean caseInsensitive;
  568. }
  569.