home *** CD-ROM | disk | FTP | other *** search
/ Dynamic HTML in Action / Dynamicke-HTML-v-akci-covermount.bin / XML / PARSER / XMLINST.EXE / make / XMLInputStream.java < prev   
Encoding:
Java Source  |  1997-11-05  |  15.7 KB  |  499 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.         InputStream in;
  62.         try
  63.         {
  64.             in = new BufferedInputStream(url.openStream());
  65.         }
  66.         catch (IOException e) 
  67.         {
  68.             throw new IOException("Error opening input stream for \"" + 
  69.                 url.toString() + "\": " + e.toString());
  70.         }
  71.         setInputStream(in);
  72.     }
  73.  
  74.  
  75.     /**
  76.      * Builds the XMLInputStream.
  77.      * Reads the first four bytes of the InputStream in order to make a guess
  78.      * as to the character encoding of the file.
  79.      * Assumes that the document is following the XML standard and that
  80.      * any non-UTF-8 file will begin with a <?XML> tag.
  81.      */
  82.     public XMLInputStream(InputStream in)
  83.     {
  84.         setInputStream(in);
  85.     }
  86.  
  87.     public void setInputStream(InputStream in)
  88.     {
  89.         String version = System.getProperty("java.version");
  90.         jdk11 = version.equals("1.1") ? true : false;
  91.  
  92.         littleendian   = false;
  93.         caseInsensitive = false;
  94.  
  95.         byteOrderMark  = false;
  96.         encoding       = "UTF-8";    // Default encoding
  97.  
  98.         this.in        = in;
  99.         this.insr      = null;
  100.  
  101.         readState      = ASCII;
  102.  
  103.         boolean setDefault = false;
  104.  
  105.         try
  106.         {
  107.             char c1, c2, c3, c4;
  108.  
  109.             c1 = (char)in.read();
  110.             c2 = (char)in.read();
  111.             c3 = (char)in.read();
  112.             c4 = (char)in.read();
  113.             if( c1 == 0xFE && c2 == 0xFF && c3 == 0x00 && c4 == 0x3C )
  114.             {
  115.                 // UCS-2, big-endian
  116.                 littleendian  = false;
  117.                 byteOrderMark = true;
  118.                 readState     = UCS2_POP;
  119.                 encoding      = "UCS-2";
  120.             }
  121.             else if( c1 == 0xFF && c2 == 0xFE && c3 == 0x3C && c4 == 0x00 )
  122.             {
  123.                 // UCS-2, little-endian
  124.                 littleendian  = true;
  125.                 byteOrderMark = true;
  126.                 readState     = UCS2_POP;
  127.                 encoding      = "UCS-2";
  128.  
  129.                 this.in       = new ByteSwapInputStream( in );
  130.             }
  131.             else if( c1 == 0x00 && c2 == 0x3C && c3 == 0x00 && c4 == 0x3F )
  132.             {
  133.                 // UCS-2, big-endian, no Byte Order Mark
  134.                 littleendian = false;
  135.                 readState    = UCS2_POP;
  136.                 encoding     = "UCS-2";
  137.             }
  138.             else if( c1 == 0x3C && c2 == 0x00 && c3 == 0x3F && c4 == 0x00 )
  139.             {
  140.                 // UCS-2, little-endian, no Byte Order Mark
  141.                 littleendian = true;
  142.                 readState    = UCS2_POP;
  143.                 encoding     = "UCS-2";
  144.  
  145.                 this.in      = new ByteSwapInputStream( in );             
  146.             }
  147.             else if( c1 == 0x3C && c2 == 0x3F && 
  148.                 Character.toUpperCase(c3) == 0x58 && 
  149.                 Character.toUpperCase(c4) == 0x4D )
  150.             {
  151.                 // UTF-8, ISO 646, ASCII, some part of ISO 8859, Shift-JIS, EUC,
  152.                 // or any other encoding that ensures that ASCII has normal positions
  153.                 readState = ASCII_POP;
  154.                 encoding  = "ASCII";
  155.             }
  156.             else if( c1 == 0x00 && c2 == 0x00 && c3 == 0x00 && c4 == 0x3C )
  157.             {
  158.                 // UCS-4, big-endian machine (1234 order)
  159.                 readState = ASCII_POP;  // Until UCS-4 is implemented
  160.                 encoding  = "UCS-4";
  161.             }
  162.             else if( c1 == 0x3C && c2 == 0x00 && c3 == 0x00 && c4 == 0x00 )
  163.             {
  164.                 // UCS-4, little-endian machine (4321 order)
  165.                 readState = ASCII_POP;  // Until UCS-4 is implemented
  166.                 encoding  = "UCS-4";
  167.             }
  168.             else if( c1 == 0x00 && c2 == 0x00 && c3 == 0x3C && c4 == 0x00 )
  169.             {
  170.                 // UCS-4, unusual octet order (2143 order)
  171.                 readState = ASCII_POP;  // Until UCS-4 is implemented
  172.                 encoding  = "UCS-4";
  173.             }
  174.             else if( c1 == 0x00 && c2 == 0x3C && c3 == 0x00 && c4 == 0x00 )
  175.             {
  176.                 // UCS-4, unusual octet order (3412 order)
  177.                 readState = ASCII_POP;  // Until UCS-4 is implemented
  178.                 encoding  = "UCS-4";
  179.             }
  180.             else if( c1 == 0x4C && c2 == 0x6F && c3 == 0xE7 && c4 == 0xD4 )
  181.             {
  182.                 // EBCDIC - We do NOT support this!
  183.                 readState = ASCII_POP;  // Until EBCDIC is implemented
  184.                 encoding  = "EBCDIC";
  185.             }
  186.             else
  187.             {
  188.                 // UTF-8 without an <?XML> tag (assuming data is not corrupt)
  189.                 setDefault = true;
  190.             }
  191.  
  192.             if( !encoding.equals( "UCS-2" ) )
  193.             {
  194.                 push(c4);
  195.                 push(c3);
  196.                 push(c2);
  197.                 push(c1);
  198.             }
  199.             else
  200.             {
  201.                 if( littleendian )
  202.                 {
  203.                     push(c3);
  204.                     push(c4);
  205.                     if( !byteOrderMark )
  206.                     {
  207.                         push(c1);
  208.                         push(c2);        
  209.                     }
  210.                 }
  211.                 else
  212.                 {
  213.                     push(c4);
  214.                     push(c3);
  215.                     if( !byteOrderMark )
  216.                     {
  217.                         push(c2);
  218.                         push(c1);        
  219.                     }
  220.                 }
  221.             }
  222.         }
  223.         catch (IOException e) 
  224.         {
  225.             // Can't do lookahead, so use default UTF-8
  226.             setDefault = true;
  227.         } 
  228.  
  229.         pos = -1;
  230.  
  231.         if( setDefault )
  232.         {
  233.             try 
  234.             {  
  235.                 if (! jdk11)
  236.                     throw new IOException("Readers not supported in JDK 1.0");
  237.                 // guess that the <?xml encoding=...?> tag will be read in
  238.                 // less that 4096 bytes.
  239.                 if (! in.markSupported()) 
  240.                 {
  241.                     in = new BufferedInputStream(in);
  242.                 }
  243.                 in.mark(4096);
  244.                 insr      = new InputStreamReader( in, "UTF8" );
  245.                 readState = INPUTSR_POP;                    
  246.                 encoding  = "UTF-8";
  247.             } 
  248.             catch( IOException e2 ) 
  249.             {
  250.                 // If there is an exception we can
  251.                 // just continue and treat file like ASCII text.
  252.                 readState = ASCII_POP;   
  253.                 encoding = "ASCII";
  254.             }          
  255.         }
  256.     }
  257.  
  258.     private void push(char next)
  259.     {
  260.         if (index == 3) 
  261.         {
  262.             System.exit(0);
  263.         }
  264.         this.next[++index] = next;
  265.     }
  266.  
  267.     /**
  268.      * Returns the next unicode char in the stream.  The read done 
  269.      * depends on the current readState.  POP states imply that there
  270.      * are characters that have been pushed onto the next[] stack.
  271.      */
  272.     public int read() throws IOException
  273.     {
  274.         // On other platform
  275.         switch( readState )
  276.         {
  277.             case INPUTSR:
  278.                 pos++; 
  279.                 return insr.read();
  280.             case ASCII:
  281.                 return in.read();
  282.             case UCS2:            
  283.                 {
  284.                     int b1, b2;
  285.  
  286.                     b1 = in.read();
  287.     
  288.                     if( b1 == -1 )
  289.                         return -1;
  290.         
  291.                     b2 = in.read();
  292.  
  293.                     return ((b1 << 8) | b2);                         
  294.                 }              
  295.             case INPUTSR_POP:
  296.                 if (index >= 0) 
  297.                 {
  298.                     return next[index--];
  299.                 }
  300.                 else 
  301.                 {
  302.                     readState = INPUTSR;   
  303.                     return read();
  304.                 }
  305.             case UCS2_POP:        
  306.                 {
  307.                     int b1, b2;
  308.  
  309.                     if (index >= 0) 
  310.                     {
  311.                         b1 = next[index--];
  312.                     } 
  313.                     else 
  314.                     {
  315.                         readState = UCS2;
  316.                         b1 = in.read();
  317.                     }
  318.                 
  319.                     if( b1 == -1 )
  320.                         return -1;
  321.     
  322.                     if (index >= 0) 
  323.                     {
  324.                         b2 = next[index--];
  325.                     } 
  326.                     else 
  327.                     {
  328.                         readState = UCS2;
  329.                         b2 = in.read();
  330.                     }
  331.  
  332.                     return ((b1 << 8) | b2);              
  333.                 }        
  334.             case ASCII_POP:
  335.             default:
  336.                 if (index >= 0) 
  337.                 {
  338.                     return next[index--];
  339.                 } 
  340.                 else 
  341.                 {
  342.                     readState = ASCII;
  343.                     return in.read();
  344.                 }
  345.         }
  346.     }
  347.     
  348.  
  349.     /**
  350.      * Defines the character encoding of the stream.  The new character encoding
  351.      * must agree with the encoding determined by the constructer.  setEncoding
  352.      * is used to clarify between encodings that are not fully determinable 
  353.      * through the first four bytes in a stream and not to change the encoding.
  354.      * This method must be called within 4096 reads() after construction.
  355.      */
  356.     public void setEncoding( String encoding ) throws IOException
  357.     {
  358.         insr = null;
  359.         String encvm; // Java VM's version of encoding.
  360.         int newEncoding = 0;
  361.  
  362.         if( encoding.equalsIgnoreCase( "ISO-10646-UCS-2" ) ||
  363.             encoding.equalsIgnoreCase( "UCS-2" ) )
  364.         {         
  365.             if( !this.encoding.equalsIgnoreCase( "UCS-2" ) )
  366.                 throw new IOException( "Illegal Change of Encoding" );
  367.  
  368.             readState = UCS2;
  369.             this.encoding = "UCS-2";
  370.             return;
  371.         }
  372.         else if( encoding.equalsIgnoreCase( "Shift_JIS" ) )
  373.         {
  374.             encvm = "SJIS";
  375.         }
  376.         else if( encoding.equalsIgnoreCase( "ISO-8859-1" ) )
  377.         {
  378.             encvm = "8859_1";
  379.         }
  380.         else if( encoding.equalsIgnoreCase( "ISO-10646-UCS-4" ) )
  381.         {  // UCS-4 NOT YET SUPPORTED!
  382.             throw new IOException( "UCS-4 not yet supported" );
  383.         }
  384.         else if( encoding.equalsIgnoreCase( "UTF-8" ) )
  385.         {  
  386.             encvm = "UTF8";
  387.             newEncoding = INTUTF8; 
  388.         }
  389.         else
  390.         {
  391.             encvm = encoding; // try passing through to VM...
  392.         }
  393.  
  394.         if( !this.encoding.equalsIgnoreCase( "ASCII" ) &&
  395.             !this.encoding.equalsIgnoreCase( "UTF-8" ) )
  396.             throw new IOException( "Illegal Change of Encoding" );
  397.  
  398.            
  399.         if (this.encoding.equalsIgnoreCase( "ASCII" )) 
  400.         {
  401.             insr = null;
  402.             readState = ASCII_POP; 
  403.         } 
  404.         else 
  405.         {
  406.             if (jdk11) {
  407.                 if (pos != -1) 
  408.                 {
  409.                     in.reset();     // This fixes a nasty bug in that InputStreamReaders
  410.                     in.skip(pos+1);   // now buffer their input.
  411.                 }
  412.                 insr = new InputStreamReader( in, encvm );
  413.                 readState = INPUTSR;                
  414.                 this.encoding = encoding;
  415.             } 
  416.             else 
  417.             {
  418.                throw new IOException( encvm + " is not supported by your Java Virtual Machine." +
  419.                     "  Try installing the latest VM from http://www.microsoft.com/java/download.htm");           
  420.             }
  421.         }
  422.     }
  423.  
  424.     
  425.     /**
  426.      * Creates a new XMLOutputStream with the proper initial state.
  427.      * XMLOutputStreams should always be created through this method
  428.      * if the output stream is to mimic this input stream.
  429.      */
  430.     public XMLOutputStream createOutputStream( OutputStream out)
  431.     {
  432.         XMLOutputStream xmlOut = new XMLOutputStream( out );
  433.         try {
  434.             xmlOut.setEncoding( encoding, 
  435.                             littleendian, 
  436.                             byteOrderMark );
  437.         } catch (IOException e ) {
  438.             // Hmm.  This should never happen because we already
  439.             // successfully created the input stream.
  440.         }
  441.         return xmlOut;
  442.     }
  443.  
  444.     /**
  445.      * Close the stream and release system resources.
  446.      */
  447.     public void close() throws IOException
  448.     {
  449.         if (insr != null)
  450.             insr.close();
  451.         else if (in != null)
  452.             in.close();
  453.     }
  454.  
  455.     /**
  456.      * Character pushed back into the stream.
  457.      * Need to be able push back four characters for auto-encoding
  458.      * detection support.
  459.      */
  460.     private int next[] = new int[4];
  461.     private int index = -1;
  462.  
  463.     /**
  464.      * The stream readers
  465.      */
  466.     private InputStream       in;
  467.     private InputStreamReader insr;
  468.  
  469.     /**
  470.      * We remember the current position in the input stream so that
  471.      * we can re-scan input after doing a reset() when setEncoding 
  472.      * is called.  We have to do a mark()/reset() on setEncoding because
  473.      * the first UTF8 InputStreamReader may buffer the input !
  474.      */
  475.     private int pos = 0; 
  476.  
  477.     /**
  478.      * Character encoding state
  479.      */
  480.     private String  encoding;        
  481.     private boolean littleendian;    // file littleendian (only applies to UCS-2 encoded files)
  482.     private boolean byteOrderMark;   // byteOrderMark at the beginning of file (UCS-2)
  483.     private int     readState;
  484.     private boolean jdk11;
  485.  
  486.     /**
  487.      * Instance varibales for Windows platforms
  488.      */
  489.     private int intEncoding   = -1;           // encoding of input stream
  490.     private boolean eof = false;              // whether the end of input stream has been reached
  491.  
  492.     /**
  493.      * Whether the document to be parsed caseInsensitive.
  494.      * (if so, all names are folded to upper case).
  495.      * Default is 'false'.
  496.      */ 
  497.     public boolean caseInsensitive;
  498. }
  499.