1/*
2 * Copyright (c) 2008-2009, Motorola, Inc.
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * - Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * - Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * - Neither the name of the Motorola, Inc. nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33package javax.obex;
34
35import java.io.InputStream;
36import java.io.IOException;
37
38/**
39 * This object provides an input stream to the Operation objects used in this
40 * package.
41 * @hide
42 */
43public final class PrivateInputStream extends InputStream {
44
45    private BaseStream mParent;
46
47    private byte[] mData;
48
49    private int mIndex;
50
51    private boolean mOpen;
52
53    /**
54     * Creates an input stream for the <code>Operation</code> to read from
55     * @param p the connection this input stream is for
56     */
57    public PrivateInputStream(BaseStream p) {
58        mParent = p;
59        mData = new byte[0];
60        mIndex = 0;
61        mOpen = true;
62    }
63
64    /**
65     * Returns the number of bytes that can be read (or skipped over) from this
66     * input stream without blocking by the next caller of a method for this
67     * input stream. The next caller might be the same thread or or another
68     * thread.
69     * @return the number of bytes that can be read from this input stream
70     *         without blocking
71     * @throws IOException if an I/O error occurs
72     */
73    @Override
74    public synchronized int available() throws IOException {
75        ensureOpen();
76        return mData.length - mIndex;
77    }
78
79    /**
80     * Reads the next byte of data from the input stream. The value byte is
81     * returned as an int in the range 0 to 255. If no byte is available because
82     * the end of the stream has been reached, the value -1 is returned. This
83     * method blocks until input data is available, the end of the stream is
84     * detected, or an exception is thrown.
85     * @return the byte read from the input stream or -1 if it reaches the end of
86     *         stream
87     * @throws IOException if an I/O error occurs
88     */
89    @Override
90    public synchronized int read() throws IOException {
91        ensureOpen();
92        while (mData.length == mIndex) {
93            if (!mParent.continueOperation(true, true)) {
94                return -1;
95            }
96        }
97        return (mData[mIndex++] & 0xFF);
98    }
99
100    @Override
101    public int read(byte[] b) throws IOException {
102        return read(b, 0, b.length);
103    }
104
105    @Override
106    public synchronized int read(byte[] b, int offset, int length) throws IOException {
107
108        if (b == null) {
109            throw new IOException("buffer is null");
110        }
111        if ((offset | length) < 0 || length > b.length - offset) {
112            throw new ArrayIndexOutOfBoundsException("index outof bound");
113        }
114        ensureOpen();
115
116        int currentDataLength = mData.length - mIndex;
117        int remainReadLength = length;
118        int offset1 = offset;
119        int result = 0;
120
121        while (currentDataLength <= remainReadLength) {
122            System.arraycopy(mData, mIndex, b, offset1, currentDataLength);
123            mIndex += currentDataLength;
124            offset1 += currentDataLength;
125            result += currentDataLength;
126            remainReadLength -= currentDataLength;
127
128            if (!mParent.continueOperation(true, true)) {
129                return result == 0 ? -1 : result;
130            }
131            currentDataLength = mData.length - mIndex;
132        }
133        if (remainReadLength > 0) {
134            System.arraycopy(mData, mIndex, b, offset1, remainReadLength);
135            mIndex += remainReadLength;
136            result += remainReadLength;
137        }
138        return result;
139    }
140
141    /**
142     * Allows the <code>OperationImpl</code> thread to add body data to the
143     * input stream.
144     * @param body the data to add to the stream
145     * @param start the start of the body to array to copy
146     */
147    public synchronized void writeBytes(byte[] body, int start) {
148
149        int length = (body.length - start) + (mData.length - mIndex);
150        byte[] temp = new byte[length];
151
152        System.arraycopy(mData, mIndex, temp, 0, mData.length - mIndex);
153        System.arraycopy(body, start, temp, mData.length - mIndex, body.length - start);
154
155        mData = temp;
156        mIndex = 0;
157        notifyAll();
158    }
159
160    /**
161     * Verifies that this stream is open
162     * @throws IOException if the stream is not open
163     */
164    private void ensureOpen() throws IOException {
165        mParent.ensureOpen();
166        if (!mOpen) {
167            throw new IOException("Input stream is closed");
168        }
169    }
170
171    /**
172     * Closes the input stream. If the input stream is already closed, do
173     * nothing.
174     * @throws IOException this will never happen
175     */
176    @Override
177    public void close() throws IOException {
178        mOpen = false;
179        mParent.streamClosed(true);
180    }
181}
182