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 package org.apache.juneau.commons.io;
18
19 import static org.apache.juneau.commons.utils.AssertionUtils.*;
20
21 import java.io.*;
22
23 /**
24 * A wrapper around a {@link Writer} that prevents the underlying writer from being closed.
25 *
26 * <p>
27 * This class wraps an existing {@link Writer} and intercepts the {@link #close()} method,
28 * making it a no-op (except for flushing). All other operations are delegated to the underlying
29 * writer. This is useful when you need to pass a writer to code that will close it, but you
30 * want to keep the underlying writer open for further use.
31 *
32 * <h5 class='section'>Features:</h5>
33 * <ul class='spaced-list'>
34 * <li>Prevents closing - {@link #close()} flushes but doesn't close the underlying writer
35 * <li>Transparent delegation - all other operations pass through to the wrapped writer
36 * <li>Useful for resource management - allows multiple consumers without premature closing
37 * </ul>
38 *
39 * <h5 class='section'>Use Cases:</h5>
40 * <ul class='spaced-list'>
41 * <li>Passing writers to APIs that close them, but you need to keep the writer open
42 * <li>Multiple operations on the same writer where intermediate operations might close it
43 * <li>Resource management scenarios where you control the writer lifecycle
44 * <li>Wrapping system writers that should not be closed
45 * </ul>
46 *
47 * <h5 class='section'>Usage:</h5>
48 * <p class='bjava'>
49 * <jc>// Wrap a writer that should not be closed</jc>
50 * FileWriter <jv>fw</jv> = <jk>new</jk> FileWriter(<js>"output.txt"</js>);
51 * NoCloseWriter <jv>wrapper</jv> = <jk>new</jk> NoCloseWriter(<jv>fw</jv>);
52 *
53 * <jc>// Pass to code that might close it</jc>
54 * <jv>someMethod</jv>(<jv>wrapper</jv>); <jc>// May call close(), but fw remains open</jc>
55 *
56 * <jc>// Continue using the original writer</jc>
57 * <jv>fw</jv>.write(<js>"more data"</js>);
58 * <jv>fw</jv>.close(); <jc>// Close when actually done</jc>
59 * </p>
60 *
61 * <h5 class='section'>Important Notes:</h5>
62 * <ul class='spaced-list'>
63 * <li>The {@link #close()} method flushes the writer but does not close the underlying writer
64 * <li>You are responsible for closing the underlying writer when you're done with it
65 * <li>This wrapper does not prevent resource leaks - ensure the underlying writer is eventually closed
66 * </ul>
67 *
68 * <h5 class='section'>See Also:</h5><ul>
69 * <li class='jc'>{@link NoCloseOutputStream} - OutputStream counterpart
70 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsIO">I/O Package</a>
71 * </ul>
72 */
73 public class NoCloseWriter extends Writer {
74
75 private final Writer w;
76
77 /**
78 * Constructor.
79 *
80 * <p>
81 * Creates a new NoCloseWriter that wraps the specified Writer. The wrapper will prevent
82 * the underlying writer from being closed via the {@link #close()} method.
83 *
84 * <h5 class='section'>Example:</h5>
85 * <p class='bjava'>
86 * FileWriter <jv>fw</jv> = <jk>new</jk> FileWriter(<js>"file.txt"</js>);
87 * NoCloseWriter <jv>wrapper</jv> = <jk>new</jk> NoCloseWriter(<jv>fw</jv>);
88 * </p>
89 *
90 * @param w The Writer to wrap. Must not be <jk>null</jk>.
91 */
92 public NoCloseWriter(Writer w) {
93 this.w = assertArgNotNull("w", w);
94 }
95
96 @Override /* Overridden from Writer */
97 public Writer append(char c) throws IOException {
98 return w.append(c);
99 }
100
101 @Override /* Overridden from Writer */
102 public Writer append(CharSequence csq) throws IOException {
103 return w.append(csq);
104 }
105
106 @Override /* Overridden from Writer */
107 public Writer append(CharSequence csq, int start, int end) throws IOException {
108 return w.append(csq, start, end);
109 }
110
111 /**
112 * Flushes the writer but does not close the underlying Writer.
113 *
114 * <p>
115 * This method flushes any buffered data to the underlying writer but does not close it.
116 * The underlying writer remains open and can continue to be used after this method is called.
117 *
118 * <h5 class='section'>Example:</h5>
119 * <p class='bjava'>
120 * FileWriter <jv>fw</jv> = <jk>new</jk> FileWriter(<js>"file.txt"</js>);
121 * NoCloseWriter <jv>wrapper</jv> = <jk>new</jk> NoCloseWriter(<jv>fw</jv>);
122 * <jv>wrapper</jv>.close(); <jc>// Flushes but doesn't close fw</jc>
123 * <jv>fw</jv>.write(<js>"still works"</js>); <jc>// fw is still open</jc>
124 * </p>
125 *
126 * @throws IOException If an I/O error occurs while flushing.
127 */
128 @Override /* Overridden from Writer */
129 public void close() throws IOException {
130 w.flush();
131 }
132
133 @Override /* Overridden from Writer */
134 public void flush() throws IOException {
135 w.flush();
136 }
137
138 @Override /* Overridden from Object */
139 public String toString() {
140 return w.toString();
141 }
142
143 @Override /* Overridden from Writer */
144 public void write(char[] cbuf) throws IOException {
145 assertArgNotNull("cbuf", cbuf);
146 w.write(cbuf);
147 }
148
149 @Override /* Overridden from Writer */
150 public void write(char[] cbuf, int off, int len) throws IOException {
151 assertArgNotNull("cbuf", cbuf);
152 w.write(cbuf, off, len);
153 }
154
155 @Override /* Overridden from Writer */
156 public void write(int c) throws IOException {
157 w.write(c);
158 }
159
160 @Override /* Overridden from Writer */
161 public void write(String str) throws IOException {
162 assertArgNotNull("str", str);
163 w.write(str);
164 }
165
166 @Override /* Overridden from Writer */
167 public void write(String str, int off, int len) throws IOException {
168 assertArgNotNull("str", str);
169 w.write(str, off, len);
170 }
171 }