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
20import java.util.Arrays;
21
22/**
23 * Places information on a communications pipe. When two threads want to pass
24 * data back and forth, one creates a piped writer and the other creates a piped
25 * reader.
26 *
27 * @see PipedReader
28 */
29public class PipedWriter extends Writer {
30
31    private PipedReader destination;
32    private boolean isClosed;
33
34    /**
35     * Constructs a new unconnected {@code PipedWriter}. The resulting writer
36     * must be connected to a {@code PipedReader} before data may be written to
37     * it.
38     *
39     * @see PipedReader
40     */
41    public PipedWriter() {
42    }
43
44    /**
45     * Constructs a new {@code PipedWriter} connected to {@code destination}.
46     * Any data written to this writer can be read from {@code destination}.
47     *
48     * @param destination
49     *            the {@code PipedReader} to connect to.
50     * @throws IOException
51     *             if {@code destination} is already connected.
52     */
53    public PipedWriter(PipedReader destination) throws IOException {
54        super(destination);
55        connect(destination);
56    }
57
58    /**
59     * Closes this writer. If a {@link PipedReader} is connected to this writer,
60     * it is closed as well and the pipe is disconnected. Any data buffered in
61     * the reader can still be read.
62     *
63     * @throws IOException
64     *             if an error occurs while closing this writer.
65     */
66    @Override
67    public void close() throws IOException {
68        PipedReader reader = destination;
69        if (reader != null) {
70            reader.done();
71            isClosed = true;
72            destination = null;
73        }
74    }
75
76    /**
77     * Connects this {@code PipedWriter} to a {@link PipedReader}. Any data
78     * written to this writer becomes readable in the reader.
79     *
80     * @param reader
81     *            the reader to connect to.
82     * @throws IOException
83     *             if this writer is closed or already connected, or if {@code
84     *             reader} is already connected.
85     */
86    public void connect(PipedReader reader) throws IOException {
87        if (reader == null) {
88            throw new NullPointerException("reader == null");
89        }
90        synchronized (reader) {
91            if (this.destination != null) {
92                throw new IOException("Pipe already connected");
93            }
94            reader.establishConnection();
95            this.lock = reader;
96            this.destination = reader;
97        }
98    }
99
100    /**
101     * Notifies the readers of this {@code PipedReader} that characters can be read. This
102     * method does nothing if this Writer is not connected.
103     *
104     * @throws IOException
105     *             if an I/O error occurs while flushing this writer.
106     */
107    @Override
108    public void flush() throws IOException {
109        PipedReader reader = destination;
110        if (isClosed) {
111            throw new IOException("Pipe is closed");
112        }
113        if (reader == null) {
114            return;
115        }
116
117        synchronized (reader) {
118            reader.notifyAll();
119        }
120    }
121
122    /**
123     * Writes {@code count} characters from the character array {@code buffer}
124     * starting at offset {@code index} to this writer. The written data can
125     * then be read from the connected {@link PipedReader} instance.
126     * <p>
127     * Separate threads should be used to write to a {@code PipedWriter} and to
128     * read from the connected {@code PipedReader}. If the same thread is used,
129     * a deadlock may occur.
130     *
131     * @param buffer
132     *            the buffer to write.
133     * @param offset
134     *            the index of the first character in {@code buffer} to write.
135     * @param count
136     *            the number of characters from {@code buffer} to write to this
137     *            writer.
138     * @throws IndexOutOfBoundsException
139     *             if {@code offset < 0} or {@code count < 0}, or if {@code
140     *             offset + count} is bigger than the length of {@code buffer}.
141     * @throws InterruptedIOException
142     *             if the pipe is full and the current thread is interrupted
143     *             waiting for space to write data. This case is not currently
144     *             handled correctly.
145     * @throws IOException
146     *             if this writer is closed or not connected, if the target
147     *             reader is closed or if the thread reading from the target
148     *             reader is no longer alive. This case is currently not handled
149     *             correctly.
150     * @throws NullPointerException
151     *             if {@code buffer} is {@code null}.
152     */
153    @Override
154    public void write(char[] buffer, int offset, int count) throws IOException {
155        PipedReader reader = destination;
156        if (reader == null) {
157            throw new IOException("Pipe not connected");
158        }
159        reader.receive(buffer, offset, count);
160    }
161
162    /**
163     * Writes a single character {@code c} to this writer. This character can
164     * then be read from the connected {@link PipedReader} instance.
165     * <p>
166     * Separate threads should be used to write to a {@code PipedWriter} and to
167     * read from the connected {@code PipedReader}. If the same thread is used,
168     * a deadlock may occur.
169     *
170     * @param c
171     *            the character to write.
172     * @throws InterruptedIOException
173     *             if the pipe is full and the current thread is interrupted
174     *             waiting for space to write data. This case is not currently
175     *             handled correctly.
176     * @throws IOException
177     *             if this writer is closed or not connected, if the target
178     *             reader is closed or if the thread reading from the target
179     *             reader is no longer alive. This case is currently not handled
180     *             correctly.
181     */
182    @Override
183    public void write(int c) throws IOException {
184        PipedReader reader = destination;
185        if (reader == null) {
186            throw new IOException("Pipe not connected");
187        }
188        reader.receive((char) c);
189    }
190}
191