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