Home | History | Annotate | Download | only in adapter
      1 /* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
      2  *
      3  * Permission is hereby granted, free of charge, to any person obtaining a copy
      4  * of this software and associated documentation files (the "Software"), to deal
      5  * in the Software without restriction, including without limitation the rights
      6  * to use, copy, modify, merge, publish, distribute, sublicense, and/or
      7  * sell copies of the Software, and to permit persons to whom the Software is
      8  * furnished to do so, subject to the following conditions:
      9  *
     10  * The  above copyright notice and this permission notice shall be included in
     11  * all copies or substantial portions of the Software.
     12  *
     13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
     19  * IN THE SOFTWARE. */
     20 
     21 //Contributors: Jonathan Cox, Bogdan Onoiu, Jerry Tian
     22 // Greatly simplified for Google, Inc. by Marc Blank
     23 
     24 package com.android.exchange.adapter;
     25 
     26 import android.content.ContentValues;
     27 import android.util.Log;
     28 
     29 import com.android.exchange.Eas;
     30 import com.android.exchange.utility.FileLogger;
     31 import com.google.common.annotations.VisibleForTesting;
     32 
     33 import java.io.ByteArrayOutputStream;
     34 import java.io.IOException;
     35 import java.io.InputStream;
     36 import java.io.OutputStream;
     37 
     38 public class Serializer {
     39     private static final String TAG = "Serializer";
     40     private static final int BUFFER_SIZE = 16*1024;
     41     private static final int NOT_PENDING = -1;
     42 
     43     private final OutputStream mOutput;
     44     private int mPendingTag = NOT_PENDING;
     45     private int mDepth;
     46     private String[] mNameStack = new String[20];
     47     private int mTagPage = 0;
     48     private boolean mLogging = Log.isLoggable(TAG, Log.VERBOSE);
     49 
     50     public Serializer() throws IOException {
     51         this(new ByteArrayOutputStream(), true);
     52     }
     53 
     54     public Serializer(OutputStream os) throws IOException {
     55         this(os, true);
     56     }
     57 
     58     @VisibleForTesting
     59     public Serializer(boolean startDocument) throws IOException {
     60         this(new ByteArrayOutputStream(), startDocument);
     61     }
     62 
     63     /**
     64      * Base constructor
     65      * @param outputStream the stream we're serializing to
     66      * @param startDocument whether or not to start a document
     67      * @param _logging whether or not to log our output
     68      * @throws IOException
     69      */
     70     public Serializer(OutputStream outputStream, boolean startDocument) throws IOException {
     71         super();
     72         mOutput = outputStream;
     73         if (startDocument) {
     74             startDocument();
     75         } else {
     76             mOutput.write(0);
     77         }
     78     }
     79 
     80     void log(String str) {
     81         int cr = str.indexOf('\n');
     82         if (cr > 0) {
     83             str = str.substring(0, cr);
     84         }
     85         Log.v(TAG, str);
     86         if (Eas.FILE_LOG) {
     87             FileLogger.log(TAG, str);
     88         }
     89     }
     90 
     91     public void done() throws IOException {
     92         if (mDepth != 0) {
     93             throw new IOException("Done received with unclosed tags");
     94         }
     95         mOutput.flush();
     96     }
     97 
     98     public void startDocument() throws IOException{
     99         mOutput.write(0x03); // version 1.3
    100         mOutput.write(0x01); // unknown or missing public identifier
    101         mOutput.write(106);  // UTF-8
    102         mOutput.write(0);    // 0 length string array
    103     }
    104 
    105     public void checkPendingTag(boolean degenerated) throws IOException {
    106         if (mPendingTag == NOT_PENDING)
    107             return;
    108 
    109         int page = mPendingTag >> Tags.PAGE_SHIFT;
    110         int tag = mPendingTag & Tags.PAGE_MASK;
    111         if (page != mTagPage) {
    112             mTagPage = page;
    113             mOutput.write(Wbxml.SWITCH_PAGE);
    114             mOutput.write(page);
    115         }
    116 
    117         mOutput.write(degenerated ? tag : tag | Wbxml.WITH_CONTENT);
    118         if (mLogging) {
    119             String name = Tags.pages[page][tag - 5];
    120             mNameStack[mDepth] = name;
    121             log("<" + name + '>');
    122         }
    123         mPendingTag = NOT_PENDING;
    124     }
    125 
    126     public Serializer start(int tag) throws IOException {
    127         checkPendingTag(false);
    128         mPendingTag = tag;
    129         mDepth++;
    130         return this;
    131     }
    132 
    133     public Serializer end() throws IOException {
    134         if (mPendingTag >= 0) {
    135             checkPendingTag(true);
    136         } else {
    137             mOutput.write(Wbxml.END);
    138             if (mLogging) {
    139                 log("</" + mNameStack[mDepth] + '>');
    140             }
    141         }
    142         mDepth--;
    143         return this;
    144     }
    145 
    146     public Serializer tag(int t) throws IOException {
    147         start(t);
    148         end();
    149         return this;
    150     }
    151 
    152     public Serializer data(int tag, String value) throws IOException {
    153         if (value == null) {
    154             Log.e(TAG, "Writing null data for tag: " + tag);
    155         }
    156         start(tag);
    157         text(value);
    158         end();
    159         return this;
    160     }
    161 
    162     public Serializer text(String text) throws IOException {
    163         if (text == null) {
    164             Log.e(TAG, "Writing null text for pending tag: " + mPendingTag);
    165         }
    166         checkPendingTag(false);
    167         mOutput.write(Wbxml.STR_I);
    168         writeLiteralString(mOutput, text);
    169         if (mLogging) {
    170             log(text);
    171         }
    172         return this;
    173     }
    174 
    175     public Serializer opaque(InputStream is, int length) throws IOException {
    176         checkPendingTag(false);
    177         mOutput.write(Wbxml.OPAQUE);
    178         writeInteger(mOutput, length);
    179         if (mLogging) {
    180             log("Opaque, length: " + length);
    181         }
    182         // Now write out the opaque data in batches
    183         byte[] buffer = new byte[BUFFER_SIZE];
    184         while (length > 0) {
    185             int bytesRead = is.read(buffer, 0, (int)Math.min(BUFFER_SIZE, length));
    186             if (bytesRead == -1) {
    187                 break;
    188             }
    189             mOutput.write(buffer, 0, bytesRead);
    190             length -= bytesRead;
    191         }
    192         return this;
    193     }
    194 
    195     public Serializer opaqueWithoutData(int length) throws IOException {
    196         checkPendingTag(false);
    197         mOutput.write(Wbxml.OPAQUE);
    198         writeInteger(mOutput, length);
    199         return this;
    200     }
    201 
    202     void writeInteger(OutputStream out, int i) throws IOException {
    203         byte[] buf = new byte[5];
    204         int idx = 0;
    205 
    206         do {
    207             buf[idx++] = (byte) (i & 0x7f);
    208             i = i >> 7;
    209         } while (i != 0);
    210 
    211         while (idx > 1) {
    212             out.write(buf[--idx] | 0x80);
    213         }
    214         out.write(buf[0]);
    215         if (mLogging) {
    216             log(Integer.toString(i));
    217         }
    218     }
    219 
    220     void writeLiteralString(OutputStream out, String s) throws IOException {
    221         byte[] data = s.getBytes("UTF-8");
    222         out.write(data);
    223         out.write(0);
    224     }
    225 
    226     void writeStringValue (ContentValues cv, String key, int tag) throws IOException {
    227         String value = cv.getAsString(key);
    228         if (value != null && value.length() > 0) {
    229             data(tag, value);
    230         } else {
    231             tag(tag);
    232         }
    233     }
    234 
    235     @Override
    236     public String toString() {
    237         if (mOutput instanceof ByteArrayOutputStream) {
    238             return ((ByteArrayOutputStream)mOutput).toString();
    239         }
    240         throw new IllegalStateException();
    241     }
    242 
    243     public byte[] toByteArray() {
    244         if (mOutput instanceof ByteArrayOutputStream) {
    245             return ((ByteArrayOutputStream)mOutput).toByteArray();
    246         }
    247         throw new IllegalStateException();
    248     }
    249 
    250 }
    251