CharWrapperTester.java revision e98fbf8686c5289bf03fe5c3de7ff82d3a77104d
1e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes/*
2e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes *  Licensed to the Apache Software Foundation (ASF) under one or more
3e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes *  contributor license agreements.  See the NOTICE file distributed with
4e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes *  this work for additional information regarding copyright ownership.
5e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes *  The ASF licenses this file to You under the Apache License, Version 2.0
6e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes *  (the "License"); you may not use this file except in compliance with
7e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes *  the License.  You may obtain a copy of the License at
8e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes *
9e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes *     http://www.apache.org/licenses/LICENSE-2.0
10e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes *
11e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes *  Unless required by applicable law or agreed to in writing, software
12e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes *  distributed under the License is distributed on an "AS IS" BASIS,
13e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes *  See the License for the specific language governing permissions and
15e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes *  limitations under the License.
16e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes */
17e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
18e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughespackage org.apache.harmony.testframework;
19e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
20e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughesimport junit.framework.TestCase;
21e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughesimport junit.framework.TestSuite;
22e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
23e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughesimport java.io.IOException;
24e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughesimport java.io.Writer;
25e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
26e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes/**
27e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes * Tests behaviour common to wrapping and filtering implementations of {@link
28e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes * Writer}.
29e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes */
30e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughespublic abstract class CharWrapperTester {
31e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
32e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    private boolean throwsExceptions = true;
33e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
34e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    /**
35e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes     * Creates a new output stream that receives one stream of chars, optionally
36e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes     * transforms it, and emits another stream of chars to {@code delegate}.
37e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes     */
38e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    public abstract Writer create(Writer delegate) throws Exception;
39e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
40e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    /**
41e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes     * Decodes the chars received by the delegate into their original form: the
42e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes     * chars originally received by this wrapper.
43e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes     */
44e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    public abstract char[] decode(char[] delegateChars) throws Exception;
45e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
46e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    /**
47e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes     * Configures whether the writer is expected to throw exceptions when an
48e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes     * error is encountered. Classes like {@code PrintWriter} report errors via
49e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes     * an API method instead.
50e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes     */
51e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    public CharWrapperTester setThrowsExceptions(boolean throwsExceptions) {
52e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        this.throwsExceptions = throwsExceptions;
53e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        return this;
54e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    }
55e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
56e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    public final TestSuite createTests() {
57e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        TestSuite result = new TestSuite();
58e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        result.addTest(new WrapperSinkTester()
59e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                .setThrowsExceptions(throwsExceptions)
60e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                .createTests());
61e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
62e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        if (throwsExceptions) {
63e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            result.addTest(new WrapperTestCase("wrapperTestFlushThrowsViaFlush"));
64e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            result.addTest(new WrapperTestCase("wrapperTestFlushThrowsViaClose"));
65e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            result.addTest(new WrapperTestCase("wrapperTestCloseThrows"));
66e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        } else {
67e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            result.addTest(new WrapperTestCase("wrapperTestFlushThrowsViaFlushSuppressed"));
68e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            result.addTest(new WrapperTestCase("wrapperTestFlushThrowsViaCloseSuppressed"));
69e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            result.addTest(new WrapperTestCase("wrapperTestCloseThrowsSuppressed"));
70e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
71e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
72e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        return result;
73e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    }
74e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
75e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    @Override public String toString() {
76e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        return getClass().getName();
77e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    }
78e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
79e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    private class WrapperSinkTester extends CharSinkTester {
80e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        private ClosableStringWriter delegate;
81e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
82e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        @Override public Writer create() throws Exception {
83e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            delegate = new ClosableStringWriter();
84e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            return CharWrapperTester.this.create(delegate);
85e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
86e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
87e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        @Override public char[] getChars() throws Exception {
88e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            return decode(delegate.buffer.toString().toCharArray());
89e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
90e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
91e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        @Override public String toString() {
92e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            return CharWrapperTester.this.toString();
93e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
94e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    }
95e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
96e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    public class WrapperTestCase extends TestCase {
97e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
98e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        private WrapperTestCase(String name) {
99e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            super(name);
100e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
101e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
102e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        @Override public String getName() {
103e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            return CharWrapperTester.this.toString() + ":" + super.getName();
104e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
105e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
106e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        public void wrapperTestFlushThrowsViaFlushSuppressed() throws Exception {
107e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            FailOnFlushWriter delegate = new FailOnFlushWriter();
108e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            Writer o = create(delegate);
109e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            o.write("BUT");
110e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            o.write("TERS");
111e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            o.flush();
112e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            assertTrue(delegate.flushed);
113e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
114e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
115e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        public void wrapperTestFlushThrowsViaCloseSuppressed() throws Exception {
116e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            FailOnFlushWriter delegate = new FailOnFlushWriter();
117e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            Writer o = create(delegate);
118e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            o.write("BUT");
119e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            o.write("TERS");
120e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            o.close();
121e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            assertTrue(delegate.flushed);
122e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
123e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
124e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        public void wrapperTestFlushThrowsViaFlush() throws Exception {
125e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            FailOnFlushWriter delegate = new FailOnFlushWriter();
126e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
127e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            Writer o = create(delegate);
128e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            try {
129e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                // any of these is permitted to flush
130e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                o.write("BUT");
131e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                o.write("TERS");
132e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                o.flush();
133e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                assertTrue(delegate.flushed);
134e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                fail("flush exception ignored");
135e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            } catch (IOException expected) {
136e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                assertEquals("Flush failed" , expected.getMessage());
137e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            }
138e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
139e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
140e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        public void wrapperTestFlushThrowsViaClose() throws Exception {
141e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            FailOnFlushWriter delegate = new FailOnFlushWriter();
142e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
143e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            Writer o = create(delegate);
144e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            try {
145e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                // any of these is permitted to flush
146e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                o.write("BUT");
147e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                o.write("TERS");
148e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                o.close();
149e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                assertTrue(delegate.flushed);
150e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                fail("flush exception ignored");
151e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            } catch (IOException expected) {
152e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                assertEquals("Flush failed" , expected.getMessage());
153e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            }
154e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
155e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            try {
156e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                o.write("BARK");
157e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                fail("expected already closed exception");
158e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            } catch (IOException expected) {
159e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            }
160e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
161e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
162e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        public void wrapperTestCloseThrows() throws Exception {
163e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            FailOnCloseWriter delegate = new FailOnCloseWriter();
164e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            Writer o = create(delegate);
165e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            try {
166e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                o.close();
167e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                assertTrue(delegate.closed);
168e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                fail("close exception ignored");
169e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            } catch (IOException expected) {
170e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                assertEquals("Close failed" , expected.getMessage());
171e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            }
172e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
173e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
174e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        public void wrapperTestCloseThrowsSuppressed() throws Exception {
175e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            FailOnCloseWriter delegate = new FailOnCloseWriter();
176e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            Writer o = create(delegate);
177e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            o.close();
178e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            assertTrue(delegate.closed);
179e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
180e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
181e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        // adding a new test? Don't forget to update createTests().
182e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    }
183e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
184e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    /**
185e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes     * A custom Writer that respects the closed state. The built-in StringWriter
186e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes     * doesn't respect close(), which makes testing wrapped streams difficult.
187e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes     */
188e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    private static class ClosableStringWriter extends Writer {
189e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        private final StringBuilder buffer = new StringBuilder();
190e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        private boolean closed = false;
191e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
192e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        @Override public void close() throws IOException {
193e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            closed = true;
194e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
195e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
196e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        @Override public void flush() throws IOException {}
197e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
198e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        @Override public void write(char[] buf, int offset, int count) throws IOException {
199e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            if (closed) {
200e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                throw new IOException();
201e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            }
202e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            buffer.append(buf, offset, count);
203e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
204e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    }
205e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
206e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    private static class FailOnFlushWriter extends Writer {
207e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        boolean flushed = false;
208e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        boolean closed = false;
209e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
210e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        @Override public void write(char[] buf, int offset, int count) throws IOException {
211e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            if (closed) {
212e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                throw new IOException("Already closed");
213e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            }
214e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
215e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
216e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        @Override public void close() throws IOException {
217e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            closed = true;
218e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            flush();
219e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
220e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
221e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        @Override public void flush() throws IOException {
222e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            if (!flushed) {
223e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                flushed = true;
224e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes                throw new IOException("Flush failed");
225e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            }
226e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
227e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    }
228e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
229e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    private static class FailOnCloseWriter extends Writer {
230e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        boolean closed = false;
231e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
232e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        @Override public void flush() throws IOException {}
233e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
234e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        @Override public void write(char[] buf, int offset, int count) throws IOException {}
235e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes
236e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        @Override public void close() throws IOException {
237e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            closed = true;
238e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes            throw new IOException("Close failed");
239e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes        }
240e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes    }
241e98fbf8686c5289bf03fe5c3de7ff82d3a77104dElliott Hughes}