1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.io;
19
20/**
21 * Places information on a communications pipe. When two threads want to pass
22 * data back and forth, one creates a piped output stream and the other one
23 * creates a piped input stream.
24 *
25 * @see PipedInputStream
26 */
27public class PipedOutputStream extends OutputStream {
28
29    /**
30     * The destination PipedInputStream
31     */
32    private PipedInputStream target;
33
34    /**
35     * Constructs a new unconnected {@code PipedOutputStream}. The resulting
36     * stream must be connected to a {@link PipedInputStream} before data can be
37     * written to it.
38     */
39    public PipedOutputStream() {
40    }
41
42    /**
43     * Constructs a new {@code PipedOutputStream} connected to the
44     * {@link PipedInputStream} {@code target}. Any data written to this stream
45     * can be read from the target stream.
46     *
47     * @param target
48     *            the piped input stream to connect to.
49     * @throws IOException
50     *             if this stream or {@code target} are already connected.
51     */
52    public PipedOutputStream(PipedInputStream target) throws IOException {
53        connect(target);
54    }
55
56    /**
57     * Closes this stream. If this stream is connected to an input stream, the
58     * input stream is closed and the pipe is disconnected.
59     *
60     * @throws IOException
61     *             if an error occurs while closing this stream.
62     */
63    @Override
64    public void close() throws IOException {
65        // Is the pipe connected?
66        PipedInputStream stream = target;
67        if (stream != null) {
68            stream.done();
69            target = null;
70        }
71    }
72
73    /**
74     * Connects this stream to a {@link PipedInputStream}. Any data written to
75     * this output stream becomes readable in the input stream.
76     *
77     * @param stream
78     *            the piped input stream to connect to.
79     * @throws IOException
80     *             if either stream is already connected.
81     */
82    public void connect(PipedInputStream stream) throws IOException {
83        if (stream == null) {
84            throw new NullPointerException("stream == null");
85        }
86        synchronized (stream) {
87            if (this.target != null) {
88                throw new IOException("Already connected");
89            }
90            if (stream.isConnected) {
91                throw new IOException("Pipe already connected");
92            }
93            stream.establishConnection();
94            this.target = stream;
95        }
96    }
97
98    /**
99     * Notifies the readers of this {@link PipedInputStream} that bytes can be
100     * read. This method does nothing if this stream is not connected.
101     *
102     * @throws IOException
103     *             if an I/O error occurs while flushing this stream.
104     */
105    @Override
106    public void flush() throws IOException {
107        PipedInputStream stream = target;
108        if (stream == null) {
109            return;
110        }
111
112        synchronized (stream) {
113            stream.notifyAll();
114        }
115    }
116
117    /**
118     * Writes {@code count} bytes from the byte array {@code buffer} starting at
119     * {@code offset} to this stream. The written data can then be read from the
120     * connected input stream.
121     * <p>
122     * Separate threads should be used to write to a {@code PipedOutputStream}
123     * and to read from the connected {@link PipedInputStream}. If the same
124     * thread is used, a deadlock may occur.
125     *
126     * @param buffer
127     *            the buffer to write.
128     * @param offset
129     *            the index of the first byte in {@code buffer} to write.
130     * @param count
131     *            the number of bytes from {@code buffer} to write to this
132     *            stream.
133     * @throws IndexOutOfBoundsException
134     *             if {@code offset < 0} or {@code count < 0}, or if {@code
135     *             offset + count} is bigger than the length of {@code buffer}.
136     * @throws InterruptedIOException
137     *             if the pipe is full and the current thread is interrupted
138     *             waiting for space to write data. This case is not currently
139     *             handled correctly.
140     * @throws IOException
141     *             if this stream is not connected, if the target stream is
142     *             closed or if the thread reading from the target stream is no
143     *             longer alive. This case is currently not handled correctly.
144     */
145    @Override
146    public void write(byte[] buffer, int offset, int count) throws IOException {
147        super.write(buffer, offset, count);
148    }
149
150    /**
151     * Writes a single byte to this stream. Only the least significant byte of
152     * the integer {@code oneByte} is written. The written byte can then be read
153     * from the connected input stream.
154     * <p>
155     * Separate threads should be used to write to a {@code PipedOutputStream}
156     * and to read from the connected {@link PipedInputStream}. If the same
157     * thread is used, a deadlock may occur.
158     *
159     * @param oneByte
160     *            the byte to write.
161     * @throws InterruptedIOException
162     *             if the pipe is full and the current thread is interrupted
163     *             waiting for space to write data. This case is not currently
164     *             handled correctly.
165     * @throws IOException
166     *             if this stream is not connected, if the target stream is
167     *             closed or if the thread reading from the target stream is no
168     *             longer alive. This case is currently not handled correctly.
169     */
170    @Override
171    public void write(int oneByte) throws IOException {
172        PipedInputStream stream = target;
173        if (stream == null) {
174            throw new IOException("Pipe not connected");
175        }
176        stream.receive(oneByte);
177    }
178}
179