home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / IFC_112 / netscape / util / Serializer.java < prev    next >
Encoding:
Text File  |  1999-05-28  |  11.1 KB  |  360 lines  |  [TEXT/CWIE]

  1. // Serializer.java
  2. // By Ned Etcode
  3. // Copyright 1996, 1997 Netscape Communications Corp. All rights reserved.
  4.  
  5. package netscape.util;
  6.  
  7. import java.io.OutputStream;
  8. import java.io.IOException;
  9. import java.io.FilterOutputStream;
  10. import java.io.ByteArrayOutputStream;
  11.  
  12. /** Object subclass that can serialize a fixed set of data types (Dictionaries,
  13.   * arrays, Vectors, and Strings) to an ASCII stream.  If the object passed in
  14.   * is not one of these types, or contains an object that is not one of these
  15.   * types, the Serializer converts the object to a string via the object's
  16.   * <b>toString()</b> method.  The serialization format is very similar to the
  17.   * output of Hashtable's and Vector's <b>toString()</b> methods, except that
  18.   * strings with non-alphanumeric characters are quoted and special characters
  19.   * are escaped, so that the output can be unambiguously deserialized.
  20.   * Serializer produces an ASCII representation with few, if any, spaces
  21.   * separating components. To get a more readable representation, use the
  22.   * OutputSerializer class.
  23.   * @see Deserializer
  24.   * @see OutputSerializer
  25.   * @note 1.0 Added several unsafe characters that will always be quoted
  26.   *           (fixed problem with archiving the @ symbol)
  27.   */
  28. public class Serializer extends FilterOutputStream {
  29.     static private boolean unsafeChars[];
  30.     static private int SMALL_STRING_LIMIT=40;
  31.     private final int BUF_LEN = 128;
  32.     private byte buf[] = new byte[BUF_LEN];
  33.     private int bufIndex=0;
  34.  
  35.     static {
  36.         int i,c;
  37.         unsafeChars = new boolean[127];
  38.         for(i=0;i<' ';i++)
  39.             unsafeChars[i]=true;
  40.  
  41.         unsafeChars[' '] = true; /* Token separator*/
  42.         unsafeChars['"'] = true; /* Strings */
  43.         unsafeChars['['] = true; /* Arrays  */
  44.         unsafeChars[']'] = true;
  45.         unsafeChars[','] = true;
  46.         unsafeChars['('] = true; /* Vectors */
  47.         unsafeChars[')'] = true;
  48.         unsafeChars['{'] = true; /* Dictionaries*/
  49.         unsafeChars['}'] = true;
  50.         unsafeChars['='] = true;
  51.         unsafeChars[';'] = true;
  52.         unsafeChars['/'] = true; /* Comment */
  53.         unsafeChars['@'] = true; /* Null */
  54.  
  55.         unsafeChars['!'] = true; /* Reserved */
  56.         unsafeChars['#'] = true;
  57.         unsafeChars['$'] = true;
  58.         unsafeChars['%'] = true;
  59.         unsafeChars['&'] = true;
  60.         unsafeChars['\''] = true;
  61.         unsafeChars[':'] = true;
  62.         unsafeChars['<'] = true;
  63.         unsafeChars['>'] = true;
  64.         unsafeChars['?'] = true;
  65.         unsafeChars['\\'] = true;
  66.         unsafeChars['^'] = true;
  67.         unsafeChars['`'] = true;
  68.         unsafeChars['|'] = true;
  69.         unsafeChars['~'] = true;
  70.     }
  71.  
  72.     /** Constructs a Serializer that writes its output to <b>outputStream</b>.
  73.       */
  74.     public Serializer(OutputStream outputStream) {
  75.         super(outputStream);
  76.     }
  77.  
  78.     private void flushBuffer() throws IOException {
  79.         if( bufIndex > 0) {
  80.             this.write(buf,0,bufIndex);
  81.             bufIndex=0;
  82.         }
  83.     }
  84.  
  85.     final void writeOutput(int character) throws IOException {
  86.         if( bufIndex >= BUF_LEN)
  87.             flushBuffer();
  88.         buf[bufIndex++] = (byte) character;
  89.     }
  90.  
  91.     private final void serializeHashtable(Hashtable h) throws IOException {
  92.         Enumeration e=h.keys();
  93.         Object key;
  94.         Object value;
  95.         writeOutput('{');
  96.         while(e.hasMoreElements()) {
  97.             key = e.nextElement();
  98.             value = h.get(key);
  99.             /*
  100.               /* Serialize the key. Test if it is a string to avoid one
  101.                  /* recursion in the common case.
  102.                   */
  103.             if( key instanceof String )
  104.                 serializeString((String)key);
  105.             else
  106.                 serializeObjectInternal(key);
  107.  
  108.             writeOutput('=');
  109.             if( value instanceof String )
  110.                 serializeString((String)value);
  111.             else
  112.                 serializeObjectInternal(value);
  113.             writeOutput(';');
  114.         }
  115.         writeOutput('}');
  116.     }
  117.  
  118.     private final void serializeArray(Object a[]) throws IOException {
  119.         Object o;
  120.         int i,c;
  121.         writeOutput('[');
  122.         for(i=0,c=a.length;i<c;i++) {
  123.             o = a[i];
  124.             if( o instanceof String)
  125.                 serializeString((String)o);
  126.             else
  127.                 serializeObjectInternal(o);
  128.             if( i < (c-1))
  129.                 writeOutput(',');
  130.         }
  131.         writeOutput(']');
  132.     }
  133.  
  134.     private final void serializeVector(Vector v) throws IOException {
  135.         Object o;
  136.         int i,c;
  137.         writeOutput('(');
  138.         for(i=0,c=v.count();i<c;i++) {
  139.             o = v.elementAt(i);
  140.             if(o instanceof String)
  141.                 serializeString((String)o);
  142.             else
  143.                 serializeObjectInternal(o);
  144.             if( i < (c-1))
  145.                 writeOutput(',');
  146.         }
  147.         writeOutput(')');
  148.     }
  149.  
  150.     final boolean stringRequiresQuotes(String s) {
  151.         char ch;
  152.         int i,c;
  153.         for(i=0,c=s.length();i<c;i++){
  154.             ch = s.charAt(i);
  155.             if( ch >= 127 )
  156.                 return true;
  157.             else if( unsafeChars[ch] )
  158.                 return true;
  159.         }
  160.         return false;
  161.     }
  162.  
  163.     private final boolean stringRequiresQuotes(char str[]) {
  164.         char ch;
  165.         int i,c;
  166.         for(i=0,c=str.length;i<c;i++){
  167.             ch = str[i];
  168.             if( ch >= 127 )
  169.                 return true;
  170.             else if( unsafeChars[ch] )
  171.                 return true;
  172.         }
  173.         return false;
  174.     }
  175.  
  176.     private final int fourBitToAscii(int n) {
  177.         if( n < 0xa)
  178.             return '0' + n;
  179.         else
  180.             return 'A' + (n-0xa);
  181.     }
  182.  
  183.     void serializeString(String s) throws IOException {
  184.         boolean shouldUseQuote=false;
  185.         boolean shouldUseArray=true;
  186.         int i,length;
  187.         char ch;
  188.         char str[] = null;
  189.  
  190.         if( s == null || ((length = s.length())==0)) {
  191.             writeOutput('"');
  192.             writeOutput('"');
  193.             return;
  194.         }
  195.  
  196.         if( length <= 8 ) {
  197.             shouldUseArray=false;
  198.         } else {
  199.             shouldUseArray=true;
  200.             str = s.toCharArray();
  201.         }
  202.  
  203.         /* If the string is bigger than SMALL_STRING_LIMIT, don't bother
  204.          * searching for unsafe character. The probably to have a space is high
  205.          * enough to add '"' automatically.
  206.          */
  207.         if( length > SMALL_STRING_LIMIT )
  208.             shouldUseQuote=true;
  209.         else {
  210.             if( shouldUseArray)
  211.                 shouldUseQuote = stringRequiresQuotes(str);
  212.             else
  213.                 shouldUseQuote = stringRequiresQuotes(s);
  214.         }
  215.  
  216.         if( shouldUseQuote )
  217.             writeOutput('"');
  218.         for(i=0; i < length ; i++ ) {
  219.             if( shouldUseArray )
  220.                 ch = str[i];
  221.             else
  222.                 ch = s.charAt(i);
  223.             if( ch < 0xff ) { /* ASCII */
  224.                 if( ch >= '#' && ch <= '~' && ch != '\\')
  225.                     writeOutput(ch);
  226.                 else
  227.                     switch( ch ) {
  228.                     case ' ':
  229.                     case '!':
  230.                         writeOutput(ch);
  231.                         break;
  232.                     case '"':
  233.                         writeOutput('\\');
  234.                         writeOutput('"');
  235.                         break;
  236.                     case '\t':
  237.                         writeOutput('\\');
  238.                         writeOutput('t');
  239.                         break;
  240.                     case '\n':
  241.                         writeOutput('\\');
  242.                         writeOutput('n');
  243.                         break;
  244.                     case '\r':
  245.                         writeOutput('\\');
  246.                         writeOutput('r');
  247.                         break;
  248.                     case '\\':
  249.                         writeOutput('\\');
  250.                         writeOutput('\\');
  251.                         break;
  252.                     default:
  253.                         writeOutput('\\');
  254.                         writeOutput('0'+(ch >> 6));
  255.                         writeOutput('0'+((ch >> 3) & 0x7));
  256.                         writeOutput('0'+(ch & 0x7));
  257.                         break;
  258.                     }
  259.             } else { /* Unicode */
  260.                 writeOutput('\\');
  261.                 writeOutput('u');
  262.                 writeOutput(fourBitToAscii(ch >> 12));
  263.                 writeOutput(fourBitToAscii((ch >> 8) & 0xf));
  264.                 writeOutput(fourBitToAscii((ch >> 4) & 0xf));
  265.                 writeOutput(fourBitToAscii(ch & 0xf));
  266.             }
  267.         }
  268.         if( shouldUseArray )
  269.             str=null; /* Pretty please!*/
  270.         if( shouldUseQuote )
  271.             writeOutput('"');
  272.     }
  273.  
  274.     final void serializeNull() throws IOException {
  275.         // We have our own magic null token!  This should only happen
  276.         // in arrays.
  277.         writeOutput('@');
  278.     }
  279.  
  280.     private final void serializeObjectInternal(Object anObject) throws
  281.                                                                 IOException {
  282.         if( anObject instanceof String)
  283.             serializeString((String) anObject);
  284.         else if( anObject instanceof Hashtable)
  285.             serializeHashtable((Hashtable) anObject);
  286.         else if( anObject instanceof Object[])
  287.             serializeArray((Object[])anObject);
  288.         else if( anObject instanceof Vector)
  289.             serializeVector((Vector)anObject);
  290.         else if( anObject == null)
  291.             serializeNull();
  292.         else
  293.             serializeString(anObject.toString());
  294.     }
  295.  
  296.     /** Flushes the Serializer's output to its stream.
  297.       */
  298.     public void flush() throws IOException {
  299.         flushBuffer();
  300.         super.flush();
  301.     }
  302.  
  303.     /** Serializes <b>anObject</b> to its stream.
  304.       */
  305.     public void writeObject(Object anObject) throws IOException {
  306.         serializeObjectInternal(anObject);
  307.     }
  308.  
  309.  
  310.     /* conveniences */
  311.  
  312.  
  313.  
  314.     /** Convenience method for generating <b>anObject</b>'s ASCII
  315.       * serialization. Returns <b>null</b> on error.
  316.       */
  317.     public static String serializeObject(Object anObject) {
  318.         String result=null;
  319.         if( anObject == null )
  320.             result=null;
  321.         else {
  322.             ByteArrayOutputStream memory = new ByteArrayOutputStream(256);
  323.             Serializer serializer = new Serializer(memory);
  324.  
  325.             try {
  326.                 serializer.writeObject(anObject);
  327.                 serializer.flush();
  328.             } catch (IOException e) {}
  329.  
  330.  
  331.             result = memory.toString();
  332.             try {
  333.                 serializer.close();
  334.                 memory.close();
  335.             } catch(IOException e) {}
  336.             memory=null;
  337.             serializer=null;
  338.         }
  339.         return result;
  340.     }
  341.  
  342.     /** Convenience method for writing <b>anObject</b's ASCII serialization
  343.       * to <b>outputStream</b>. Returns <b>true</b> if the serialization and
  344.       * writing succeeds, rather than throwing an exception.
  345.       */
  346.     public static boolean writeObject(OutputStream outputStream,
  347.                                       Object anObject) {
  348.         Serializer serializer;
  349.  
  350.         try {
  351.             serializer = new Serializer(outputStream);
  352.             serializer.writeObject(anObject);
  353.             serializer.flush();
  354.         } catch (IOException e) {
  355.             return false;
  356.         }
  357.         return true;
  358.     }
  359. }
  360.