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 / XMLOutputStream.java < prev    next >
Encoding:
Java Source  |  1997-12-02  |  11.3 KB  |  395 lines

  1. /*
  2.  * @(#)XMLOutputStream.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. import com.ms.xml.parser.DTD;
  9.  
  10. import java.io.*;
  11.  
  12. /**
  13.  * 
  14.  * A writer specifically designed for dealing with XML, incluing
  15.  * XML encoding as well as liitleendian files, and XML namespaces
  16.  * and white space handling.
  17.  *
  18.  * @version 1.0, 6/10/97
  19.  */
  20. public class XMLOutputStream extends OutputStream
  21. {       
  22.     /**
  23.      * The state enumerators
  24.      */
  25.     static final int OUTPUTSW    = 1;   // The different states that concern the write() 
  26.     static final int UCS2        = 2;   // The UCS2_BOM implies a ByteOrderMark must be written.
  27.     static final int UCS2_BOM    = 3;   // NOTE:  All encodings that can use OutputStreamWriter
  28.     static final int ASCII       = 4;   //        fall into the OUTPUTSW state
  29.  
  30.     /**
  31.      * Builds the XMLOutputStream.
  32.      * It is created as a standard UTF-8 output stream.
  33.      * A subsequent call to setEncoding will specify the correct
  34.      * character encoding required.  XMLOutputStreams can be built
  35.      * by using the createOutputStream method contained in XMLInputStream.
  36.      */             
  37.     public XMLOutputStream( OutputStream out)     
  38.     {
  39.         String version = System.getProperty("java.version");
  40.         jdk11 = version.equals("1.1") ? true : false;
  41.         outputStyle = DEFAULT;
  42.         littleendian  = false;
  43.         savingDTD = false;
  44.         mixed = false;
  45.         
  46.         this.out      = out;
  47.  
  48.         newline = System.getProperty("line.separator");
  49.         indent = 0;
  50.         
  51.         // We default immediately to UTF-8
  52.         try {
  53.             if (! jdk11)
  54.                throw new IOException("Writers not supported in JDK 1.0");
  55.             
  56.             this.outsw = new OutputStreamWriter( out, "UTF8" );
  57.             writeState = OUTPUTSW; 
  58.             encoding   = "UTF-8";
  59.         } catch( IOException e ) {
  60.             // If there is an exception (there should never be) we can
  61.             // just continue and treat file like ASCII text.
  62.             this.outsw = null;
  63.             writeState = ASCII;
  64.             encoding   = "ASCII";
  65.         }
  66.     }
  67.  
  68.     /**
  69.      * Pass-through of inherited OutputStream method.
  70.      */
  71.     public void flush() throws IOException
  72.     {
  73.         if( outsw != null )
  74.             outsw.flush();
  75.         else
  76.             out.flush();    
  77.     }
  78.  
  79.     /**
  80.      * Pass-through of inherited OutputStream method.
  81.      */
  82.     public void close() throws IOException
  83.     {
  84.         if( outsw != null )
  85.             outsw.close();
  86.         else
  87.             out.close();
  88.     }
  89.  
  90.     /**
  91.      * Defines the character encoding of the output stream.  
  92.      */
  93.     public void setEncoding( String encoding, 
  94.                              boolean littleendian, 
  95.                              boolean byteOrderMark ) throws IOException
  96.     {
  97.         outsw = null;
  98.         String vmenc = encoding;
  99.  
  100.         if( encoding.equalsIgnoreCase( "UTF-8" ) )
  101.         {                       
  102.             vmenc = "UTF8";
  103.         }
  104.         else if( encoding.equalsIgnoreCase( "Shift_JIS" ) )
  105.         {            
  106.             vmenc = "SJIS";
  107.         }
  108.         else if( encoding.equalsIgnoreCase( "ISO-8859-1" ) )
  109.         {         
  110.             vmenc = "8859_1";
  111.         }
  112.         else if( encoding.equalsIgnoreCase( "ISO-10646-UCS-4" ) ||
  113.                 encoding.equalsIgnoreCase( "UCS-4" ))
  114.         {  // UCS-4,  NOT YET SUPPORTED!
  115.             throw new IOException( "UCS-4 not yet supported" );
  116.         }
  117.         else if( encoding.equalsIgnoreCase( "UCS-2" ) )
  118.         {
  119.             // We only set the byteOrderMark if the initialEncoding is UCS-2.  
  120.             // Otherwise the flag is irrelant and we should ignore it.
  121.             if( byteOrderMark )            
  122.                 writeState = UCS2_BOM;
  123.             else
  124.                 writeState = UCS2;
  125.  
  126.             this.encoding  = "UCS-2";
  127.                 
  128.             if( littleendian )
  129.             {
  130.                 this.littleendian = true;
  131.                 out = new ByteSwapOutputStream( out );
  132.             }
  133.             return; // we're done ! UCS-2 is handled manually.
  134.         }
  135.         else
  136.         {
  137.             // If none of the conditionals equate to true, 
  138.             // the inital writing is treated like ASCII text.
  139.             writeState    = ASCII;
  140.             this.encoding = "ASCII";
  141.         }
  142.  
  143.         if (encoding.equalsIgnoreCase("ASCII"))
  144.         {
  145.             outsw = null;
  146.         } 
  147.         else
  148.         {
  149.             try {
  150.                 if (! jdk11)
  151.                    throw new IOException("Writers not supported in JDK 1.0");
  152.                 outsw         = new OutputStreamWriter( out, vmenc );
  153.                 writeState    = OUTPUTSW;
  154.                 this.encoding = encoding;
  155.             } catch( IOException e ) {
  156.                 // If there is an exception (there should never be) we can
  157.                 // just continue and treat file like ASCII text.
  158.                 throw new IOException( vmenc + " is not supported by your Java Virtual Machine." +
  159.                     "  Try installing the latest VM from http://www.microsoft.com/java/download.htm");
  160.             }            
  161.         }
  162.     }
  163.  
  164.     /**
  165.      * writes a character to the stream according to the current writeState.
  166.      * There is an extra state for a UCS-2 requiring a ByteOrderMark so as to
  167.      * avoid an extra conditional in every UCS2 write.  The ByteOrderMark will only
  168.      * be written once at the beginning of the file.
  169.      */
  170.     public void write( int c ) throws IOException
  171.     {
  172.         switch( writeState )
  173.         {
  174.             case OUTPUTSW:
  175.                 outsw.write( c );       
  176.                 break;
  177.             case UCS2:
  178.                 {
  179.                     int byte1, byte2;
  180.  
  181.                     byte1 = c >> 8;
  182.                     byte2 = c & 0xff;
  183.  
  184.                     out.write(byte1);  
  185.                     out.write(byte2);                    
  186.                 }
  187.                 break;
  188.             case UCS2_BOM:   // We need to put a ByteOrderMark to immitate original file.
  189.                 {
  190.                     int byte1, byte2;
  191.  
  192.                     writeState    = UCS2;  // We now go to a normal UCS-2 state.
  193.  
  194.                     out.write( 0xfe );
  195.                     out.write( 0xff );
  196.     
  197.                     byte1 = c >> 8;
  198.                     byte2 = c & 0xff;
  199.  
  200.                     out.write(byte1);  
  201.                     out.write(byte2);                    
  202.                 }
  203.                 break;
  204.             case ASCII:
  205.             default:
  206.                 out.write( c );
  207.                 break;
  208.         }
  209.     }
  210.  
  211.     /**
  212.      * This method writes out the fully qualified name, using
  213.      * the appropriate short name syntax. For example: "foo:bar".
  214.      * @param n the name being written
  215.      * @param ns the name space which defines the context
  216.      * in which the name was defined.
  217.      */
  218.     public void writeQualifiedName(Name n, Atom ns) throws IOException
  219.     {
  220.         Atom sns = n.getNameSpace();
  221.         Atom shortName = sns;
  222.         if (sns == ns)
  223.         {
  224.             writeChars(n.getName());
  225.             return;
  226.         }
  227.         if (sns != null) 
  228.         {
  229.             if (dtd != null && ! dtd.isReservedNameSpace(sns))
  230.             {
  231.                 shortName = nameSpaceContext.findNameSpace(sns);
  232.                 if (shortName == null)
  233.                     shortName = dtd.findShortNameSpace(sns);
  234.                 if (shortName == null)
  235.                     shortName = sns;
  236.             }
  237.             writeChars(shortName.toString() + ":" + n.getName());
  238.         } 
  239.         else
  240.         {
  241.             if (ns == null)
  242.                 writeChars(n.getName());
  243.             else writeChars(":" + n.getName());
  244.         }
  245.     }
  246.  
  247.     /**
  248.      * Write the given string.
  249.      */
  250.     public void writeChars( String str ) throws IOException 
  251.     {
  252.         int strLen = str.length();
  253.  
  254.         for( int i = 0; i<strLen; i++ )
  255.         {
  256.             char ch = str.charAt( i );
  257.             if (ch == 0xa)
  258.             {
  259.                 int nlen = newline.length();
  260.                 for (int j = 0; j < nlen; j++)
  261.                     this.write( newline.charAt(j) );
  262.             }
  263.             else
  264.             {
  265.                 this.write( ch );
  266.             }
  267.         }
  268.     }        
  269.  
  270.     /**
  271.      * Write out the string with quotes around it.
  272.      * This method uses the quotes that are appropriate for the string.
  273.      * I.E. if the string contains a ' then it uses ", & vice versa.
  274.      */
  275.     public void writeQuotedString( String str ) throws IOException 
  276.     {
  277.         char quote = '"';
  278.         if (str.indexOf('"') >= 0 && str.indexOf('\'') < 0) {
  279.             quote = '\'';
  280.         }
  281.         write(quote); 
  282.  
  283.         int strLen = str.length();
  284.         for( int i = 0; i<strLen; i++ )
  285.         {
  286.             int ch = str.charAt( i ) ;
  287.             if (ch == quote) {
  288.                 if (quote == '"')
  289.                     writeChars(""");
  290.                 else
  291.                     writeChars("'");
  292.             } else {
  293.                 this.write(ch);
  294.             }
  295.         }
  296.  
  297.         write(quote);
  298.     }        
  299.  
  300.     /**
  301.      * Write a new line or do nothing if output style is COMPACT.
  302.      */
  303.     public void writeNewLine() throws IOException
  304.     {
  305.         if (outputStyle == PRETTY && ! mixed) {
  306.             int nlen = newline.length();
  307.             for (int j = 0; j < nlen; j++)
  308.                 this.write( newline.charAt(j) );
  309.         }
  310.     }
  311.  
  312.     /**
  313.      * Set the relative indenting level.  Eg indent(+1) or indent(-1).
  314.      * The indent level controls what writeIndent writes.
  315.      */
  316.     public void addIndent(int offset) {
  317.         indent += offset;
  318.     }
  319.  
  320.     /**
  321.      * Write the appropriate indent - given current indent level,
  322.      * or do nothing if output style is COMPACT.
  323.      */
  324.     public void writeIndent() throws IOException
  325.     {
  326.         if (outputStyle == PRETTY && ! mixed)  {
  327.             for (int i = 0; i < indent; i++)
  328.                 this.write('\t');
  329.         }
  330.     }
  331.  
  332.     public static int DEFAULT = 0; // default
  333.     public static int PRETTY = 1; // pretty output
  334.     public static int COMPACT = 2; // no new lines or tabs.
  335.  
  336.     /**
  337.      * Set the output style (PRETTY or COMPACT).
  338.      */
  339.     public void setOutputStyle(int style)
  340.     {
  341.         outputStyle = style;
  342.     }
  343.  
  344.     /**
  345.      * Return the current output style.
  346.      */
  347.     public int getOutputStyle()
  348.     {
  349.         return outputStyle;
  350.     }
  351.  
  352.     /**
  353.      * The stream readers
  354.      */
  355.     private OutputStream       out;
  356.     private OutputStreamWriter outsw;
  357.  
  358.     /**
  359.      * The system newline character
  360.      */
  361.     String newline;
  362.  
  363.     /**
  364.      * Character encoding state
  365.      */
  366.     boolean      littleendian;
  367.     String       encoding;
  368.     private int  writeState;
  369.  
  370.     /**
  371.      * output styling.
  372.      */
  373.     int outputStyle;
  374.     boolean jdk11;
  375.     public boolean mixed;
  376.  
  377.     private int     indent;
  378.  
  379.     /**
  380.      * needed for saving in correct format
  381.      */
  382.     public DTD dtd;
  383.  
  384.     /**
  385.      * needed for saving in correct format
  386.      */
  387.     public NameSpaceContext nameSpaceContext = new NameSpaceContext();
  388.  
  389.     /**
  390.      * whether we are in the scope of saving a DTD or not
  391.      */
  392.     public boolean savingDTD;
  393.  
  394. }
  395.