ItemOperationsParser.java revision 02d8984e7452e6c3a3aaec189aca885c3f206a32
1/* Copyright (C) 2011 The Android Open Source Project.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *      http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16package com.android.exchange.adapter;
17
18import com.android.exchange.eas.EasLoadAttachment.ProgressCallback;
19
20import java.io.IOException;
21import java.io.InputStream;
22import java.io.OutputStream;
23
24/**
25 * Parse the result of an ItemOperations command; we use this to load attachments in EAS 14.0
26 */
27public class ItemOperationsParser extends Parser {
28    private static final int CHUNK_SIZE = 16*1024;
29
30    private int mStatusCode = 0;
31    private final OutputStream mAttachmentOutputStream;
32    private final long mAttachmentSize;
33    private final ProgressCallback mCallback;
34
35    public ItemOperationsParser(final InputStream in, final OutputStream out, final long size,
36            final ProgressCallback callback) throws IOException {
37        super(in);
38        mAttachmentOutputStream = out;
39        mAttachmentSize = size;
40        mCallback = callback;
41    }
42
43    public int getStatusCode() {
44        return mStatusCode;
45    }
46
47    private void parseProperties() throws IOException {
48        while (nextTag(Tags.ITEMS_PROPERTIES) != END) {
49            if (tag == Tags.ITEMS_DATA) {
50                // Wrap the input stream in our custom base64 input stream
51                Base64InputStream bis = new Base64InputStream(getInput());
52                // Read the attachment
53                readChunked(bis, mAttachmentOutputStream, mAttachmentSize, mCallback);
54            } else {
55                skipTag();
56            }
57        }
58    }
59
60    private void parseFetch() throws IOException {
61        while (nextTag(Tags.ITEMS_FETCH) != END) {
62            if (tag == Tags.ITEMS_PROPERTIES) {
63                parseProperties();
64            } else {
65                skipTag();
66            }
67        }
68    }
69
70    private void parseResponse() throws IOException {
71        while (nextTag(Tags.ITEMS_RESPONSE) != END) {
72            if (tag == Tags.ITEMS_FETCH) {
73                parseFetch();
74            } else {
75                skipTag();
76            }
77        }
78    }
79
80    @Override
81    public boolean parse() throws IOException {
82        boolean res = false;
83        if (nextTag(START_DOCUMENT) != Tags.ITEMS_ITEMS) {
84            throw new IOException();
85        }
86        while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
87            if (tag == Tags.ITEMS_STATUS) {
88                // Save the status code
89                mStatusCode = getValueInt();
90            } else if (tag == Tags.ITEMS_RESPONSE) {
91                parseResponse();
92            } else {
93                skipTag();
94            }
95        }
96        return res;
97    }
98
99    /**
100     * Read the attachment data in chunks and write the data back out to our attachment file
101     * @param inputStream the InputStream we're reading the attachment from
102     * @param outputStream the OutputStream the attachment will be written to
103     * @param length the number of expected bytes we're going to read
104     * @param callback A {@link ProgressCallback} to use to send progress updates to the UI.
105     * @throws IOException
106     */
107    public static void readChunked(final InputStream inputStream, final OutputStream outputStream,
108            final long length, final ProgressCallback callback) throws IOException {
109        final byte[] bytes = new byte[CHUNK_SIZE];
110        // Loop terminates 1) when EOF is reached or 2) IOException occurs
111        // One of these is guaranteed to occur
112        int totalRead = 0;
113        long lastCallbackPct = -1;
114        int lastCallbackTotalRead = 0;
115        while (true) {
116            final int read = inputStream.read(bytes, 0, CHUNK_SIZE);
117            if (read < 0) {
118                // -1 means EOF
119                break;
120            }
121
122            // Keep track of how much we've read for progress callback
123            totalRead += read;
124            // Write these bytes out
125            outputStream.write(bytes, 0, read);
126
127            // We can't report percentage if data is chunked; the length of incoming data is unknown
128            if (length > 0) {
129                final int pct = (int)((totalRead * 100) / length);
130                // Callback only if we've read at least 1% more and have read more than CHUNK_SIZE
131                // We don't want to spam the Email app
132                if ((pct > lastCallbackPct) && (totalRead > (lastCallbackTotalRead + CHUNK_SIZE))) {
133                    // Report progress back to the UI
134                    callback.doCallback(pct);
135
136                    // TODO: Fix this.
137                    //doProgressCallback(pct);
138                    lastCallbackTotalRead = totalRead;
139                    lastCallbackPct = pct;
140                }
141            }
142        }
143    }
144}
145